From 1782d8b856bb008091a34394f2044c0d743fefec Mon Sep 17 00:00:00 2001 From: tchataigner Date: Wed, 17 Jul 2024 16:25:03 +0200 Subject: [PATCH] feat: sync committee proof server (#100) * feat: sync committee proof server docs: rust doc + recompile program feat: compile program feat: proper count for signers feat: remove setup logger feat: committee change proof server * docs: fix necessary RUSTFLAGS * Update ethereum/docs/src/run/setup_proof_server.md Co-authored-by: wwared * refactor: integrate review Co-authored-by: wwared --------- Co-authored-by: wwared --- ethereum/Cargo.lock | 7 +- ethereum/core/Cargo.toml | 1 + ethereum/core/src/crypto/sig.rs | 2 +- ethereum/core/src/types/store.rs | 3 +- ethereum/core/src/types/update.rs | 2 +- ethereum/docs/src/run/setup_client.md | 2 +- ethereum/docs/src/run/setup_proof_server.md | 42 +++++ .../artifacts/committee-change-program | Bin 318404 -> 317028 bytes ethereum/light-client/Cargo.toml | 4 +- ethereum/light-client/src/bin/client.rs | 93 +++++++---- .../light-client/src/bin/server_primary.rs | 76 ++++++++- ethereum/light-client/src/client/mod.rs | 72 ++++++++- .../light-client/src/client/proof_server.rs | 147 ++++++++++++++++++ ethereum/light-client/src/lib.rs | 1 + .../src/proofs/committee_change.rs | 58 ++++++- ethereum/light-client/src/proofs/mod.rs | 115 +++++++++++++- ethereum/light-client/src/types/mod.rs | 2 + ethereum/light-client/src/types/network.rs | 68 ++++++++ ethereum/light-client/src/utils.rs | 29 ++++ ethereum/programs/committee-change/Cargo.lock | 1 + .../programs/committee-change/src/main.rs | 6 + ethereum/programs/inclusion/Cargo.lock | 1 + 22 files changed, 680 insertions(+), 52 deletions(-) create mode 100644 ethereum/docs/src/run/setup_proof_server.md create mode 100644 ethereum/light-client/src/client/proof_server.rs create mode 100644 ethereum/light-client/src/types/network.rs create mode 100644 ethereum/light-client/src/utils.rs diff --git a/ethereum/Cargo.lock b/ethereum/Cargo.lock index c4397dd6..2e88eec1 100644 --- a/ethereum/Cargo.lock +++ b/ethereum/Cargo.lock @@ -561,7 +561,7 @@ dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "itertools 0.10.5", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -1435,6 +1435,7 @@ dependencies = [ "ethereum-types", "getset", "hex", + "serde", "sha2 0.9.9", "ssz_types", "thiserror", @@ -2637,7 +2638,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" dependencies = [ "cfg-if", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] @@ -3619,7 +3620,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" dependencies = [ "anyhow", - "itertools 0.10.5", + "itertools 0.12.1", "proc-macro2", "quote", "syn 2.0.70", diff --git a/ethereum/core/Cargo.toml b/ethereum/core/Cargo.toml index 9abb03dc..a16b67ed 100644 --- a/ethereum/core/Cargo.toml +++ b/ethereum/core/Cargo.toml @@ -11,6 +11,7 @@ anyhow = { workspace = true } bls12_381 = { workspace = true, features = ["experimental"] } hex = { workspace = true } getset = { workspace = true } +serde = { workspace = true, features = ["derive"] } sha2 = { workspace = true } thiserror = { workspace = true } tiny-keccak = { workspace = true, features = ["keccak"] } diff --git a/ethereum/core/src/crypto/sig.rs b/ethereum/core/src/crypto/sig.rs index ea075e9e..5b5c6f68 100644 --- a/ethereum/core/src/crypto/sig.rs +++ b/ethereum/core/src/crypto/sig.rs @@ -274,7 +274,7 @@ pub const SYNC_AGGREGATE_BYTES_LEN: usize = SYNC_COMMITTEE_SIZE / 8 + SIG_LEN; /// index who signed the message as bits and the actual signature. /// /// From [the Altaïr specifications](https://github.com/ethereum/consensus-specs/blob/81f3ea8322aff6b9fb15132d050f8f98b16bdba4/specs/altair/beacon-chain.md#syncaggregate). -#[derive(Debug, Clone, Getters)] +#[derive(Debug, Clone, Eq, PartialEq, Getters)] #[getset(get = "pub")] pub struct SyncAggregate { sync_committee_bits: [u8; SYNC_COMMITTEE_SIZE], diff --git a/ethereum/core/src/types/store.rs b/ethereum/core/src/types/store.rs index 417f547d..e4adc28c 100644 --- a/ethereum/core/src/types/store.rs +++ b/ethereum/core/src/types/store.rs @@ -180,7 +180,8 @@ impl LightClientStore { .sync_aggregate() .sync_committee_bits() .iter() - .sum::() + .map(|&bit| u64::from(bit)) + .sum::() < 1 { return Err(ConsensusError::InsufficientSigners); diff --git a/ethereum/core/src/types/update.rs b/ethereum/core/src/types/update.rs index e6d52a68..8f56c974 100644 --- a/ethereum/core/src/types/update.rs +++ b/ethereum/core/src/types/update.rs @@ -30,7 +30,7 @@ pub const UPDATE_BASE_BYTES_LEN: usize = LIGHT_CLIENT_HEADER_BASE_BYTES_LEN * 2 /// A data structure containing the necessary data for a light client to update its state from the Beacon chain. /// /// From [the Altaïr specifications](https://github.com/ethereum/consensus-specs/blob/81f3ea8322aff6b9fb15132d050f8f98b16bdba4/specs/altair/light-client/sync-protocol.md#lightclientupdate). -#[derive(Debug, Clone, Getters)] +#[derive(Debug, Clone, Eq, PartialEq, Getters)] #[getset(get = "pub")] pub struct Update { attested_header: LightClientHeader, diff --git a/ethereum/docs/src/run/setup_client.md b/ethereum/docs/src/run/setup_client.md index dd168524..e6d9ad91 100644 --- a/ethereum/docs/src/run/setup_client.md +++ b/ethereum/docs/src/run/setup_client.md @@ -10,5 +10,5 @@ With our deployment machine properly configured, we can run the client. ```bash git clone git@github.com:lurk-lab/zk-light-clients.git && \ cd zk-light-clients/ethereum && \ - RUST_LOG="debug" cargo +nightly-2024-05-31 run -p light-client --release --bin client -- -c -b + RUST_LOG="debug" cargo +nightly-2024-05-31 run -p light-client --release --bin client -- -c -b -p ``` \ No newline at end of file diff --git a/ethereum/docs/src/run/setup_proof_server.md b/ethereum/docs/src/run/setup_proof_server.md new file mode 100644 index 00000000..99fbdb7e --- /dev/null +++ b/ethereum/docs/src/run/setup_proof_server.md @@ -0,0 +1,42 @@ +# Deploy the Proof Server + +We have two components to deploy for the Proof Server to work as intended. The primary and the secondary server. +There is no particular order in which they should be deployed, but here we will deploy the secondary and then +the primary. + +For best results, the primary and secondary servers should be deployed to **different server instances**, so that +proof generation can happen in parallel if necessary. + +## Requirements + +Make sure to finish the [initial configuration](./configuration.md) first. + +## Environment variables + +- `RUSTFLAGS="-C target-cpu=native -C opt-level=3"`: + - `-C target-cpu=native`: This will ensure that the binary is optimized + for the CPU it is running on. This is very important + for [plonky3](https://github.com/plonky3/plonky3?tab=readme-ov-file#cpu-features) performance. + - `-C opt-level=3`: This turns on the maximum level of compiler optimizations. + - This can also be configured in `~/.cargo/config.toml` instead by adding: + ```toml + [target.'cfg(all())'] + rustflags = ["-C", "target-cpu=native", "-C", "opt-level=3"] + ``` + +Make sure to launch the proof servers with `cargo +nightly-2024-05-31`. + +> **Note** +> +> One can also set the `RUST_LOG` environment variable to `debug` to get more information +> about the execution of the server. + +## Deploy the primary server + +Finally, once the primary server is configured in the same fashion, run it: + +```bash +git clone git@github.com:lurk-lab/zk-light-clients.git && \ + cd zk-light-clients/ethereum/light-client && \ + SHARD_BATCH_SIZE=0 RUSTFLAGS="-C target-cpu=native -C opt-level=3" cargo +nightly-2024-05-31 run --release --bin server_primary -- -a --snd-addr +``` diff --git a/ethereum/ethereum-programs/artifacts/committee-change-program b/ethereum/ethereum-programs/artifacts/committee-change-program index 17b4162584a8eb5229f28f32717590ad1a547ad1..af1ab096d4ddda1e85cb54157bcdf539e5946db0 100755 GIT binary patch delta 101971 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 PdM(peoDjReprHQ;s!&JL delta 103281 zcmbTf34B!5**|{n+_{sPWD-JdHnL9U%uM#MTp%i7Cu&e6Xsn{Pf<+^06$C3(YsH!I zX2Ozy$N}R@fn>sx1hgZjRcI$l00or(UeUKLR9i&5fZ~E$%kuv|_f8hn_I=+!pU-ga zeV*q$XM3LKJm)#*+`QkV?&?ujx-4V=@RfBaJL(yu zpLGb{`FksK{?misW3P<2#D4$$*BNQU-^=Q?GF9sRrCcPjkNt0z371-vt9Y4Q8(v*a zsW-f;AyZ-WtSWtL#Y1Xkg3Zf#`-`i@b81)vT<;67?xC~;w2#{uJ&UHkwbAh7tU7@? z)r2h8@YS)h>ICHT^sEwhrr&DuCyM>)Gk+9g?89JiwXiCTyB@PtM?5G7?2w8k<#9^Y z#Y(GCW=X0|vb3hkp!csZ>Ac-NhYY%Hl&XvSwhM)U?q0>Di#3ZS8D+k{J9yRlIP2Or z?pklH_HrubcJ9WrQKaG(|D913K+XSc%N4~|Z9{8pnXKW9zLaV!wdRFY+_T=QD$K@x z>#e**GWZEa$y1e()u{4G3su?DU{m_*kYP42oE>jou24qFRBd}JGuro%2xeAh#VezG z8quv*V+e#5Loolh+2U|!1`P>skF08V!fH_!Kclv{K1cn@7FJV#DB<53{nFI;2cfKs z_Z`a@C0SQ0{_%2cy4aSLEsYf`vvMr4F`_FgM`5Go+RFgWQv4(3+UpF0%2nApN~~P2 zU0@(snOxgwl9b4`s|^yLm`^kwx%LH>4(axz;L) z=dv9YtH^IyfMh;@sLv&5G z%@{3vT$+C<>Rgox5JM53y-oyO8JE+ua%!D1iV4bMRp|xq=fKy>%secM#8xPg^@jK+ z)NU|oR)QuJr-`MD!>(%SudSno$sEbaeR5&zREL}_zRD>d-LBgFsGN;)Ux;!0ddRZG z+Uydm_M-B(zHN|S6j0M&V5zt+_iFFMsx6hN)>I>BL!M&QBaMb|*(+07RO{`1YgOeksYhW>5zb%@4{RUP6W5w*s;r_sk8Y5J)i0?E z`u60xa~Q9$c?G(p>M_$Ik%due9cBgk9A*XPQ)T-r@PW87AN?#=<`+we;#c{FlZI3` z1bgJ~ACbevWsqBK5;_@p;?_U1!9R%ff^w-{_zQ}qhsB)*g~KZ}rB;%CFv5mb_l*}% z6jV!2aj+n_=36BVa`NvpON%(ox+}?MY4s?rw|`h)6BUJ2_uC35ptN;`xyeRpJyg#y zO4?gkdXr&uV@r?bDPL4?r}~EX5zJA>9M-W)RUGkQXKnMC-^$%qXu@hXkHxI#$<=@2Ie)vgU#!GQJ}%c7 zrtfMnyHNN&oziUKdc7#YG}Vu^u6w}hO4rL2Cif*WHN(rQJIIvS!r}_AH$b`)R-X8# z9-XhPwZ~B3WZk<|63eb++Y2^1J|0rpw9)2T#FY-Ig{5j|4_tO+5!qcDp z&kXvufq_B{s9?Db#=yuIeo9pM@=e>e{b^C}D@aqm{)S~)EP}qPA$$ixc+6;Rm-yW0 zH8UN0M!1SIiVbE9*Q&OaG&0h+3sQce+Ga^&R!NaqRGbOrSY1r&v!mFXWfM+|9(Oy;*_)Z?DzRiPp+8^)yCJ)uN4&xY9U`jCmJkLZz zA5&9m;|4DLjk+ma->h4<0kFT=^-acNMtOlHSSS2N>0)L1g$byq2!|WR!E(*(Qfe_XpX8#m|sY^VDSwBj2#nmswQ06tJ>cC|B*ZRC-GiYb^c#T zDQEz7FQ$L*WkWQ+_7yR9L)?hdFy*Lt@?{prN(1E2VKH4irt6RNyF+# z*t}rsM0!`@I+?Hy22A!yp6b~Q@HKb$tg+BYZips5&l(oA1HW>TTo&S{f8gC-4ZNy^ zH)sl^n)=ZOTPkLbN}d$Vle?0Dt7i??F3MNh1DjLl8{>=Y=z$4%=E5(Oy80O&u6N{%htQ)NVE9IsPaVr zexp7P?{?uD?JJ0-7)3j%=nAkrn9F_FaDC%cCU-V7kmQNrXj?%nk;qJv)NDfQTbd2e zMT~HB%un_l+*nBl|Q>xeFdOrnQb{jeDMVad)q)AziVkNx7)lO{hfu z_r>us?!<#XXjpI^)#-;KTk~BQ{2QtS3M%NJ)u@PPeV!cI2U9_55-LI|DpW>kv&1V5 zytrR0&k44{vu3Iz72ezPMOd5_#+eTnJ2YRVKTYpOD^UPiR{$mqnG59hX0#Xulo8A* zfN)~i*epxEQN#<3#;VS!tD^U*l1IjS*u?9x+a$`(|~spu4lFl!m6x| zD$ft03$9kf`|dOHHtn#iW(u;Mj|#W|1`R zamgn(jPvKSpY$sc7Age|t4V3t0=c5sqfW0A=f-8-%I=p-YdqC`Wa4V?tVW4YksC}E zA)DY}YJwBS7t5RHXx$n6jUIv8kg7D2Dg>|5+pmNmo;#;1P?5$)s2&NF|K|-0S}1); zJTSg2&mWbM%^WcQgL?NeNX+X@?oO2SPVt-Z#esvQ|7@PBoIE2OR!hTzFfzv;ZdgE) z8Vj7~XO)xhmSp;FN#wpK*#~^L+qScLEB$J}ucdoycr+X)ea8YgiM1N#1n=bQ?t(k- zXMDpJqw-japZZF5SgmI5V~e=E22w#98Jr}7)tLeRZSh8ap(a{wak8H_ET{tsyWcFw z3!X_Xg2OhRDIs-NSSU4{l5eWKI8y4(9L2P(bc?uPASO$B@e_U9t zR#fhvLZQ(hY4In==~LqLLq@{?H_+Qa|0U=#5(Ygw)Zm{Krynu`{trCn5NsZUj95~h zJcW$NV2C9Oq>RCYXv@SKCE=Rrz9KeO?{tiTcAuxUgB1>DkGro9pn%tL*z#1m5j4;z?Z)g1R&1w3s z2~2(s)5B{wVX<&GnXVl9^`GOpnM_xX-1jttZA99MuBY^6vYANt!KtV8kBkRg^zaYL zoGthLi|im$*t{}IbIG_OEtkLm=>!Q3kWQtU2I)-_gaYZ^WOOs-zMo6TpD7<`mQXnH z$kbo<lPtsd~B%rJ`@JO3jo@YVsOB@1Vi0XjpIMM(nt2@iC+wpU|;jp^hPN=X8+MH*^Jbyu;w0DGdSDB<&NF&5EPD!r}Ws7H*OVQ)c;<<*N znOxeN9X(Mg>7`P(c+w%srJ*c!#8OH7@r9C_zntl%y;;#8Pm<)ynk+RxSrShzlcIm$ zB5Akpfx7lOwcEeKP$T^y4I@Zt14M$-HzW%$6Dc)Wfr?L@(GQ!^SDz&Iw8E`gym&)2 z;S;7cfkjDgfwuTV;Qs8=ZqJ4E^d@Q_G(sqP6J=koOHHsPaQB~?_F)|c`_GAZp8#5> z-SKM(1m117dB{_seQ*&319{47xR$sDg1}tr+vAyLd!30FYDyu5cDXNuER0p|!?;z^ z`#6%)M7gh)-feQ$<7omJew-eqe_w^Ay-Vc#hfbJNj z$ajj$1Aj&OJ^&!dAgQJbkknHJNa~T+lGFn}ol?-vCaFidkm#ubB=uAQq6a*J#*3|& z;<1$wL<%1N5e~WwmoT4TC13d1KG{?uR=Y=It zi^dl&=kCW+G{=)nq_033e?yu0eRQ#1(*E|e6#a3RxO&R81d+UYjArc-ZBynZsLQYW zvu14UxU%tgwKoK z^OmH2{2)x0lpp>0w4^qEc1}yWP143 zMqCB>Rj7+p6#eLrs7sLWMbVGHh7a_wb5ZA0lJ-)aBreKjn)SLG&3c)nUiW#8mWWPU z_6X|oLuS zZP=qI=!_J-_>9Ci>>R__9T>wqj*Q{$-DCKcGh_Is6Jz++e2sS{Ykb4pv3%XkvApBJ zINrW!>`)okD|+4H5qQyIaq-o;f%fDvdi%67^5*4Z^vwsx$Pwf&!7{3Inx=1Aj^)(> zP47$|D|b&DtA>-A-o1RRT-G=C+Y;BTj}qhI#0i{+BgFztc=CczyZ{pwdV{|$?N`u`w*y{X!LQJV9H zML+v5Nv;Z|Y7hU0$s>DHqVwOyxYeXaPl=rRf`InW2}uk59YcJuMq2=}`q}TX#>Z6o z;2|mc^&h0@zy2si&mNJgVdYe1_S$GNHV@V>xA3+zGLKZseB-kgzImF=J15D!d#22{ z&6WB3MG9|QuJFhag>O8e@XjMvzWIQaZ#!e<-6yQPEkA*;Pfp+)=lake4ANVxGieM`W|tDxSIbCW*|-WhTOVWa%&5>$KmXt>QJzESyMD<5@K&? z3%!z=kG(;7G5^{eU+m>L>vd$YhOXS=q;HBi>Eu{X|G-Bn$^hdQuf;xX;kY>GSl^IR z`Y`l)qW07|^;ExMioGiKUgJv&#>if()-_o@;-A;#Tdar%%*<0skkAr-A0i{F0?}Qn zh^aH(2{B_|;#Xq+_uUr%i(>w)Y*|>WV%zr%(n$2M9vH}x=n(IIzc`U?iyPf>BK2DA zhjfcaMA@~MS%RCW)fRuNXt}mf-l96h)@w^92UnfnGI;BT8TEGzwkEg^ruG}H@&AHj zX-*}nw(v-icio7Db;eA$M9jJ_Tg;lCEN;1Ogfyh4iGwwDBvMT+{#9bm^c?Je@UoDL zdLhA3CnZs2O&@7t&EksbZcA(l5t-eTE!Is}0!F*z+`=|oi`M@gSQ)^G2d`lW}(3B0YNul!5^)*ikCC@5 z8>3^K<#I)nEBZ9OBCN@kwPR(EGFJEYp;w+ylg0BtxHDkZd=k1V9i?VR``Y0NNSW$S zzZ#~=F@N;bY^IU7iFKE_7*=`3*>k9Ewzj*IkyVb?cEIR_vbD)u@P0efCL8Y~qPsV< z=oi56rT3Z<(aD{7znw*Q)4Ppwe%ez?g%XcL0 zO?t1vdpq9mV9`DFF6Blq>1Jf*qp=N?KPP$#3jFj|7JY@@YjWkP-Zbs1txS9PPm(re zE725U28hGpp#TRy0hd0qMvcC?jYZF4>fN=CG6N>XVe=T_Xk4(WQKP$1iO=7XqOYNs zyfvA+ClrU>#4aly@H};oI^{`T8Seylqh#kDMsOOjph~pDBZ+mSH9=s zIH?jdWhL*NS;@QSR`P9&DlwCe~+Gq2~tgzqpe9Y&zg z5MFPeUd}6?&(Yhv@jSNzd2{5>$_l_M5E0Ii+q?0M%KJdGrLq$6 zN*?e4v85X?bE{B5j=s6F3WzEckfT$}s-Dl*$)}(x+o&Y_Cw!W`sB#cKPH&!8+lSuW z&cvxNhG|z}Jy+7p)$JG}8~h;UIlp!akqr!8^rt(R_9cD`chcG~FM9v4A(Jn#!sr7p zGi|{O(1Mqk_P|Rl`q{5ZAV+BTZD;DkXT>`=kG7F!L?09>Kgxv9(?>F#6E#w3rN^9U>E$NE-b@3B+VA9k>Lhw z55EB|-wj3HL(BR+?PssC=;=LB++9pNvzJ8|>_YWkVbKTnVomqet1ua_QbTj$7-59g z=fD`2@QUB%@a`ie2%zV|7b@YtKj-oF7^$k^1#p_mxUVP+PFFd1eNx0Xo+-!NRm3+x zTP|7>uqgg>5%0w4bwdP8eg|WEq&zM?uWRt5V8&FwzPpmUrjO3xZIH#P$I^J@*$VEy zB?F#VrP^$Pe9y!%*JSX{X_e|SJA*R@x@$7{wwaY`GTzrGkK`ryWw1Ex*%6*5H^Ng&cv>+MejInxim`4S_*j4+2Rz>Qk3$)FcaENZ-3J8fnfo2ol24?BN z+mMm)Hqhbygbj3rPe7#zp8z=Wbtix>UGF@Bcj{cMA!$i&TA}Bm@TQNB_jH6qo&$gn zS^)nx;cW26=in~g8yaFAf}MKpoH6p`8Dms*vRqp?h6i^tHM5E7wRvN-*&U4kqWi2| z>l`E2gqb|KVvIg{;u!5%go)bE RQ~-g zs4o0`7+<=)iVku$Sh3?!zw&-L(tS=}bK;!5J6XKjFfp*UKhplqJr&(o!{v$^H|?wF z#ZpS^eA-tD-q24+q9AwBmH_PuBKBEdf3F+~!|4wd=#^4lyuhD8jS(B&34H3$c**E| z*;7-5II@Jw{7f_koY?3!H*=y6@^z z$Q0~=IVi{me|QQRPV6BrrC=NU<=4ot;vKvYtulC5tao9&Eymtoed>MO=DQaYXOZ~w zHXdLt$U-`bYAhuO6fwRXH7Hu@ui{|Q?DRm<#-=HX#twr~w6TqeqFqoiD!PQ4gJ${G zBWj5)U)px8PrXa_N>+-9>t4xDQE@%s4OxvL_w^=3kHLuhB*hR5U$0%e{cWcEqy+J} zO_GaiY--kGDSG=mSTxrtc*X#Q60|$ML|S7Nt&8F6)g(keOpxS~UaOkDgy|(AyLM+b z*3!K;@dScFZpkKAJ%XT38lHO+D z@J-CB1oQG2Szz z=xMz3-b}O`(W~JLp8A+7l&Ql<&^?HS?0SJ| z=iZm(a`*_l?t-oi^!fEv9FV|Vf`1kWTF3c&k9NjnV! z54>kjZ(4L$BRp#0-BJ-Mm=?Wky5Tv))5*cRxb{w;^~!xr6N~P2)hwKf7L&(AdvDHN zDt1JoeH-zMNY%E_|DoDmWZKj{_`NJePfbTpVm*RIxYnPqLj38@oPaiUFGzCP014K@ z10-;is;7T{zN$=L5bbLN>prRa_BZZVZR5k*KGb7s^B`-iod@#4C#gPlJ&u|l+8yn~ z3IlQTYG1+()%MfP+7vi7Q~ueXFF+%Xj8n|EVBuYTEQo_&Xqi5f<}CSO}vOBvx#)L;bh3?B$gK!az)f2lE_ z4c`WSDZvfDCp02XLya<@eMX~<=K#8jT8^$lh6)2=KGW`4)sX0c=ztCQdTf9gTPspy^R#+ zaLe9ZG~49$)JX`zwOobVqHs=@h2@9~=C}i~toXj;91W+3<&1p^dhI_F)Fx-9)^vdJ zRM@$)-b^mNh|D{(sOTOC!`NKKLz_X+YZ9!86D*GtAZ=?Q22zs2AlN?2VDM-h zkF;$C;cHC%BXN9)QWSu+E%?GHRKUbH#__s|FXnh?dxQvjO@gL4K{(C;X-uV7Vy|sOm zyvZ|)OK;%{$!()}>gQs@J(makry!68+|}-cpyhM7ktTDGk>Vs3r8Nb-q}_?$bQ-;x z)MYbzGm%pC7ScAy4jX#Y#3#q`&Nv=vTN2?zCVp5PpMo?%4DisFOaxN0QGnECHws{s z0*|z9m{EXOdTFP(`?eflJ+y)+Rlqf&pLJ4sR~c}XySl~zp2po>0X+1kaUTJsbnY=y6&dkflMY5in2wBu z&j2H&z3Ip}7HI|;A+5mxsFAL`+j0dU!0KHTJq*4wTAg##;14?tifbiL1fHaf?1{3j~g9;$6$wPjm zQZDk-dp@@tsRjA)9TnTVKz>@%; zCPvE5vwEH-CzyyvjFb|_yD6K)k=cT1$-vpivVA4z|+bJUI}addx!3!y<_$=uE`CYl%)>wC#nwTaFOU7tt!ro;X33X{Rt+-99oiM#!L0>cwbn_IOF$_zu%>{z2UMHk`6+@&eiwH$sqZ z$&2p26NfDlr055k_`SoT;}5b18&aGh6i9+d`lg?jDW8>j2H4VM6ps1kgcbRdk>85^$-q02AF!oP z*bUW$9pMX((bj~bxpq7gwv2i&-3|L-nZ)h z5ebQ29I1SLm2lmEr6o94%(?%{n`0w!bkf=rpNryKs+QK3#8_`oJf!P%AFYF{Y;PSKPPwKKwpQG@viPPIfB!1TMwkkyIUlAV;wP1 zmm@8VKXM?wEwt0Fz6~!;KY&z`+8#Mz7v!ZqveVwCAF0Pnlf=V2k=mrTMx<{yG2VE@ z-WEP#7t0r8)aA3LwxanZJq+2BXj9X$QqT*+l zNZn%I&&u)t)5iaMjQ>9~{x5ux;8TtN?|-m7(4E$Q?pJPKwn{D!xdty)_N^6(R%bOX z)Ts*YRKC9jqn%n!^xQLD^y>jdI??f<8R&Q??`k>Ax6U}n*G>RC9T-Gw2ZDDe*T z*Cr?9Tkz{ba8pRjv&Fcd54W&KM8)#_fHALnar@ci zxCjSx#cxdfl1{MnJ2g?K_|4CYq*LO(pO4J=R8<;JqswGE&tb|h`$QaHo}XYg|08k5 zL)Y5>CVQvCJ|u~a4^6Q6kBRpmx+IhRnX;)$A|{Cb-j2O1>R0C9VyeawUUm`uzB0Lg zR4LWHIBJixwe&1-wO?(Tj_3HO-iFw;|DdIsF1fqmReN^fb^_ES7-4Pr@etCGf)N)l zEpl1>yT#!}ZcFgLRy~{(@V`=h%yE~hEWDkRs(Sk!FC) z@*8Iy@~a2zdf!gF{^}7s5A7cL90C4>L!0-eB)`knPPF1(RBX|g_^W773q93LqP94P| z(}y7m1gj9g2nMq^h$M#1!e?;V!q0eT7gC5zcbBLR1!k~RI=igRewL{t91wWe`~4i%3|3FFIdP zMdIQ@Q!0ZiA(if;*w6lFfppb9HAEgQkVlJrV47Xu1$lgJrd{3-dEB=ga<|B?E>33p z0m$T)8}0HHd+@~rtE6AjW0x;|$F5)Tp`EjhqPnrnQnFDrASv4@9&N0$RBRN#Z5$EM zH_pi)x<97VsNPH2bxQ%SxVONZFWJL74;9M1 zw-7OImtIoif=|okNCnpG>)rEC{%9{f>ybJ6)Q!A-CnjCyGACab%*ep(JIYv)%#0h) zTQ%Anu8q&cqYloJYcUhoVkVx9nRr=ORsT#Zd-k8xJx;_C>ky>dQDx>=8fX?WTd?pA zbFfo!P|t7|u2-iwB7oObrB1gZfJbR#4dZxdTh@YT9*`>lJOt!&N*ftR+Sc+62$xDc z*fmO>?vUCZrHpWpkhZO;7%5_lGU8I^|E83|LC!M^k4+QrEy)Q4CmNR0N|tiDVJU@` zEakJNrF4*`tklR-3I|!rNt$6P6UkCe)5ubaM6#4KHN#RSk)@ogk);$#WGNTn^TQa8j{lYcp-e)Ai~l1kY;Bab&HpKDuo;w)cw;A|VUWO@F% zww5zzdCQTrZOuE+@<&gcYg=*P9AB~MY}<12(h`q_mC(3V#WHEUXq;$#Xk2JKXdI~j zzuE8Nx$ANr7IX78HcqU3df+XnQQrq*d6<*ZF(;*CPD;m|l#XFb$DEXoF-gOml@=H0 zTS2lzWx#^gIGUPWGrR#4lPnUN+o6Z*>&d}dDkgo6AnJ&L} zBwc^)M7mgu6WAZEHB7j8a;X%3zZ4VCTM}PSw8d0S9?w9Jzcv&zLa z1#&G;O4sf|MCU_9biOnKJzLxrRWn~V>f48$8SxefKp;inhiMqA>57xz9cN{^~nDL?nD zp{sbfoo0pSSCbVtM1SQK@X4#5Ijd6))wKh*z8)SQ{Bq9@?77tB4MW%vh98c38GaQ` zajvD@;=kE&%jX-j>Ai+qJ}NdR?jhIwpWN&BjOA8y+%>=KfAp?R`Xv;sOh-HN>7)z{ zYd-cA^Jz~}%vwGfjwn3p(p=NfA3V>mHemiY;a9sY?0`sJak0h!V^P0ij3w4Lm?`$k zw=)HMk%>#c6~|Y6-@;xJ{zosd#I}pZN5?3^U!n@)0G?;>U8V_-aXlnDtmjf-xHYDdM5Wc+qOrnvPj@dHT?aba2&y70IT@ zF0rr`;`n3Z1Hol-L`BjZ(|Wnj`QOQqx{}#2Wh}-tHiohGvgg~yReGdn~Y8i-lB#b#)VRN zJ_W4?EH)1?Bb)#!i^GEV0A_|0Xq{zt_*}kql4@uTR{8L5($yq%NY`9d+_Eeet$1cz5YKxiQ}ThCd{G>tJMBjLRvy*NH_k9H%7*#>join%r@~ zaH8}GrlC+54wmjOkt5T_@a`QYdIVQcNLM?%O7sBvLz{P$#5?_3v(x9C>mMwUM1%Fv zzZoo+Z075mJ~ZEdpoitbxYUMM%jHXrYuv`&Zq>ZUZ9VTE_b4WrgR&V5%Op$Bb`KV> z;dH2tCEl^u%Y7B;oOR-Q>%{anBdts4QYSNfGnrEOW)|ZeyNsQzO?n!zW!L~CSfbQs z?Zlo{@6A+L17e0kT_x8ej%)k;nMN<-{@vU<) z!Y&#XV}uQn?|N3;u%>W^f9l`>$9{I+01xp`$tRUD$a1MUz`9fkTIo zvq~L8&MNhX;jGRW88^Du52)gIsBqxhIvo6KTr6%Jn(^POaPSzd1zShaRe|7mH3=aH z?ED~rbGPY|Q^W(;4a|mm;<$SZUnk-jfyHaM)Lf+X>s)YjgVS0kx*pHJ+Q`I4nw8AB zQ^RpMGa``DPDkTJ@-kcDlt^x{D5)wIt*Z6&b7FaNVgs{Y zB1W~kXZYbiVX@LTF(0eZeD0j1aJCtH+`q;i_iK>G8Nkf}?xB2rcT0Yov>DR(YwU5q z#<=`@2+;C>Vj{J{rk<8NmY=4$yl757gYW<4s0+o`)~wVpzExmUJm^9!L6*g#)*FWt z(CDHVjv(SZVxqYCi4hJQpv1F9DHF4vz}=q=k^NMWh&(a-(irZ!{+Q`~KVb6iDGckF zJTfM7-v~1@`SgxFz0YCX9+!8QVwHX&mfzds&Z9DiBwQBLp3Ir@7S$7W@a*I{4s|t`a(WK_6tG zOG*8#%%Kk2!~m<gHM%MVxNlkr|Pbg_k~A*Ii~Od z0Xi{6fZ~_S&l8|OlB~pq=zc2Ih>4kU@;C6>DK2~3T^9VnSdgxvacs%&xgKXgnrzsK z{GAcNEm} z_yvnd+iC4sW^~|v$i-T7F7-FY_3*rW|CU}3`a`1r8DFB5*ZLf7n#P_N!Dk9*#^R;l zJhb%hjEOnZ7cyg|6zzCBF$CBU|#Ljwpm zaGEhU9YJK~a(q1uzP@jdRE;At7>oyl zWDq2SAQ=RRg?En!gYjT69t;TYlsoF2dWX{~w_Pz;-e{jIZ{0grZ(A}~-gd=3`d0fr z`sTg&=-ZauBe(CpSC3qAFA5pYHz5DIBjd4yh7$sxxVaB88qc3`jNleGcirpeRlVZq z+S~xUx_@q_RHx4*A~;CT5VEyexN*q-4Yt`_{wvM>n~qhq3XGj=y$ca_ z_^$o-IJQ=fIE&G`V!5NG7_BRY8;_$LJF~Hul#Si3Y&j5d!ZWEW0YM1}N_Ytf%0N&7 zf+`S1oVblrR|X#&+*YZpL;;m3 zfcAL{P=Nwepa2!X!DuILu+&unUj=*>Do}t5AO;3})sca-YEe3NR!JUf@;q$M<5c&w zdB|H=h|Gn^T!_qtd|hWM-|%uOZ{L^7JN}po0vGTu;9bDG_$J`D0N(}t)<32SN6R#P z9LO7WU?W*+5wET*3sfwh$KBhpJKF~V{Ui3ATHIjahJ3lf0%6F`G_XkHoA#xl2*Ts5 zOm#)T7Xk0#?Z9sUejV^z|492&9^gH|dw}Q2pN{+pOYB2f!oYLjIq+OAJupw~=+2t} z6?L`FGFE{-j-eC|k#AOZs{(D7mYp#7B*%ZFz4sq>E(;{dWp<{Q0nmAQhTM^m5xwvx zDLP@Y)X;L5BzD6C>BFs~Q`lf$f$jBFzg_Ik9fUO!j3qSK6$~R7qW}@#?~fikWbE~; z*T2m~<^+7C1L^gDgyv*2wK|hUn?ArH+B}?Cs6AxtW&iY{t^+cUVUb z3M%W!onQh-sZNmLJ6u?xCJ&vw1R@NrE+djSg}$pZZ{@J(Oj=AG>6=Gplxg8-23N)g zH*8?e--rz+pE%xulT?QPQL^5{%Onq^z{Oo*EMdG7OjTLDc4Zq!XB2*$k-q?pcy*rI zWR-_X8@Lq2B`pUsQ^?)m=)(DC*QA45? zpGY)ss!%Au;W{gn?e7oPXI)}`mqMX9Ow!MVsUkxUIT6-}L^0>MAX z-lSsPJGEH$H5co?eVBl{Fahnr1hgL$(BW+0OMou{z65y8Kfsp(UjclTfiDHV6!=o$ zG5=uy&%Fcl&wk85hanS5Wx$sKUj{tpAK=S?uK+$!MFjgQbZ>Ko?3-Gl`;ssLbzuV9 zfeC0oCZNN4z?TDG4tzQAn16ux0PhCgMfjvj6i|r*Dp3IDpDxTlJ23z3$NX~`o=y_Z z6iuxHz6yBEKfsp(U$MX7YP6sL?ogpznZV_;%Q=W~9A|1FhzmhXhapqJEEUXB!7LSc zjNMch@bKRDxxg$H%u>M&$4d{3b6r;ks`jVxio9r-=Ldw}P9`C^XzI6S%|9r=N4=BNh8b~f*M z8{flgYPvyl{*LJtd$^dhK5v2;u>Y|aQD?~5R4?l6MV-B9QzqJklcoDJ(WXq`eZc#G z_W>_nUC$Fk@%e>4kNL$tDYms=ZU!e9_%McrVcq=;-8c?+P-^-opH4Dxzu5Yt`kErX zq4q-VSzpB0MK0tWYc9kYuPMBJ;e~vIcLHw@U&K4Y7d5otSk}1dhL@`*+E_yhX=H4w z98uuC;`|r9Ujv>luG)5Upn96b)THGI%}QP^X|{xxz+6mCL`xFUl0?jL@Fjb&-Hp%Q zbi9LevPnt6Cjp-Xd=lRT{1)K5fZs~^efSn|b23CN8KRcVw?Zqsdf;wCGdI069(Wv? zZgv9i1b&*6cYvTB1RFrG?w#>i37_}zW1Uo#PWzCzNFP)VFU1$RFhhjl#ovZ2a;dTL zJ;2e|=*0ui6$FCKsH35e)fiUQM#GBJ^KqTTumiUbx(c22O~u_-=%Nq0SOQ%vBVB}! zdiOzBlc0-JOA%#&jsow3uDMCoprhV>&{gQ9Z)zFxLr0;DCBT;fAMip+z5Af7&`Do& z1sK3d1VI&at%7t7I_gb=u0kh$&A2oRT`Ykv`hfQsc<5>pbQL=3n_3B(rr0=s&_y@t zB6QTd54sAS^i8co1)!tQ#S-Al4i^MK0A<}r$_o8Ta>>Qe!{Veuxnv(UN2j`U_r5~8 zEm_j})Iv37`NbjyBfkRs{U!H|k=qU+&JKoEhH^t+0gEh$={^koJ)X*4r;%<2Y#QRO zP8{XV8_s<*px*?82jqSzkg7VCPt%rGVG08f+Npb+p`l4`v=*xZXs8DoS^^C=G<+X4 zd}>_7rxw9-rNJ1O8tx_yhh}@5p~28(Uy=uSXf`ys3>scS8V=2dCIiob=iHYJ0uKn> zAaETPzxZY5_+`wE=$3COvNAyTBY27#i3VM4CMHR7 zf3zVGf{(C$4)?8@%RLL{a`(7-+!MZs`@;7OJ#%}sal`HX=WTm1Z_x)j=_6h}h*#l` z$ebuW8z1Kk->NyDXGfbh+&chl=2fv|Us2RoZ6Y{8Nn(pHh1i`QQ_V zJQTMvVSUF4MlKV=2T~70ff&=j!FK^2XkC(T;Cyri-mBnV1z?8FuV(u9x2`k%)P(aK z7CJX&S1zDW2!ZK^J$;BTHq|y5$F_`K2wr2hw6=j-I@pu3DF}C9I z?FljKr4a#yqRb=YxIX2> zVQUtY&F-x|IrI!3=0wn8_hUnZ@l9w|f4+9S++sz(kC%@UlBm#7t zncyB2zSfWluf*}mse#Go=ufwo_4H3Xc`r9im}~Y+fBTrZ+hhyRRng`_arBoNB3HE@ zqPHwAdg7&*F9?L;B}=eiw5Nz8J|&cf;PY%ZMhIurb{qZDr2L6eb+PiUP)=F~COKO# zw2Sm0c+~9hKFB%>4)@D@%sT)FY_LMCKsXSs4LA}>q!15xVr`K4T?&&c44N1wVr0OL z*7F(m7vwKAc?`i1D4jnmp65QZas$1U7Z^Yo;?=Lc{&X4z6*E8NS?ZtpkIvto*x&gi z4gK;fAm4_D8zRi!Rh@oBff-^*Ywi|KZ2QDN)lHESNRl!{C+G;a|AX&Rib0)Ve=sVk z^xZ5Jza_3n6ND3rgq0NG@WHOn__}5U|DJ|Rf)8DGl;e0QzD~Mj0zO(sqP+0cZw%vm zLV-v>ezS#~l?~d!IQsYWfT7w{d%J+*#BH1zuC>+Pe!em>gmfuN8Jj`9W$c1^X&20t zVG?0+vbS{D=Y0#z)y#{l5N^L-ywz7Z647|x7D+K17RFc7A}Ol1esGn8eSY-R_xtrp z)cq!ZKquEl()k7@-3S*ewR_CSw|TV9ltupxvkjQ)=o?jrh4O!Clw}KJM-x-qfSnG0 z++TMig{33A+^>g*8sc5|^tuD0aW#}Peq8kH_FE|f9Wo%sLJ3PM{#FBKUw3@={#8HY zp=v9!QhdBZvxM^43512g#jL!R?H?OlZaia3&*y;`6oGJh87M} zaMF!x4p@kWpKiaghiV4bREA2U%P)eJ$La^_y>1O;VZOyMS1?gVmne`OeD6S2RvNRA z>cn51I9d3jc>lLm{$K;5sW2I}ag!0oY}FT^td#g^s9eg@IPSA`4{{{_T1ArAiyER$YyVj@@3Gg6d`-3gO({&{5j50V4e?iPwp#~O z>W&}_n+1f?WB9@a^T(C47GEj(&sUdFxpLJypLAh_YIPxHBl<-GhWy*Q_C?>Q0bR2x zX`d+n?_SjqR7&Wt*zk22UCd?X=s+(aBy^*4;7Ty; z1lM0op>zDaad%3a-jRZ{{5ZD%VqBMN3wfj?W!}kOyr5b~QN0tey?;*j;p-s2Izxi{ z|8No3lo$Ep>k6`R(^XBgqy$uJyx zWOfR7VZA{2z1%zF6PNOaIXcQ8-A6`IJ+EEqEWXB8{-7QY<%DFN)%)C+v*9U*|LRD&Od-{+z zg{KJRIZJ#NJ_Dy8*)fWqLof;0_vj0+#`ceK?w#K8H;&dCqd)&`%fRSAgTGN=bTs@l zqodh0vy5;2NgrdTXQ$#c75_*q z_bKSMFm7FwA+WKmOD*`LW0&F#;_d>=r7f%WWLfdS<@(lhbFBs9!abucY;ttYp4%l$ zaHN>CCr_N*TlPa+o@gRR9Q*A}{T$@=sw80_a(G-e!Y*$7Ef@&AXFbk&Ndr9%lYySc zI|f`RN)i5oN8G_-y{+r6kV6Tke>_ zCFe8tRVE}r@jUOW5-ay*eCJ&F7Nv3W;R7+_z=#ByVGQ;6@pl&U9{-b^*P8mEG$MN7 zz>K_v_lza-2r=sKH3>)0ox6j*nl2vr`_3BEu1j5z)Y zzH|Asuzy;W$(}&hS&=iDx-FjTaZ&fFJ14l>lyMw2ISNwB;{sKLCrNJ(C`FGGwHrV+u8Dtmy-nYs8 z7suHh{}Qr$q0xuqzk_U?(TAf4nfa@}O~oq3U%$H29m@s^TdE;z{Ld3qs`R;D`e@lf zL)nVOb^ly(i9et8NOerdA9@I-f;?GR%x~_-rKSSACS+?;>BtLG5;9Xp%uyU6JYP?? z_!TkdYhR`xdq*Z#_e%I9f<{5Z#ip;za{MXCNGB0(vZn^CC@jqkbj&G^e|9}D_h^UOIKzF zOHk_{QjLYe;2ar6WVK%A01flD-szC4EX>{dgabuRX#JG~MF#U)PdcRQEi9|`ibUxm zi(hSRM4A~K_8qLww!W1}temZ1B}#9X1l_WiE zVV}1?;51tMgi{(*75hkS#zj>nG|H?of;c9N6Mz{kjWbdPj2&uTGJMl?4wG?EG^5Zvpm=a@l{iE=i$wy&30`J)~W4 z5WP@BLtwG}t;bU&cV_T)qCdY4{?}SP!!U%wzSdd8q|8jVE6%e3baK3%ue7!dqkf4I zZ-NWlmW=n~Z@+_nXX{~u{$-*U@wp~W|5BWONMFQ$)mo7X5edH7TAxaF*dC|x45>r3 zb>%P#N6(%oo<7sPwU2CuXT+TVG z_x-$o_>5s+uM(S~Ph?mt;To0;%N&(^ zM5NRKJ2)#ut2#PN#*4(Ob#!nZ3&rU=FbeJYA}0i%!I>up!`}=IC(KSDPm7q-=kPAU zZDL_aFBo9i&_+pc``G{n;xTATdEJ=}>8bcaLP&2EPq*&&>$0bavmrfyC@H=J1mRhC z5Db*JK|$$YkYT*ca#M@uXZldu$Z(Bvh>0N+*l92e%i%%! zSZ0@os*~MAus<}nsIMXSz@(f#OsvV!>#5oeO|++3q|@M{42D2S+G7SV*9BDR216Q| z4CW(BvoDU;Vs3t4#aS0Y8d^kz*fEv?fI(mdT(%Mng}y`Z<&8m`Ii8aEeg zTf{N|*GncW^6PLKh`a`Rvwm80xF&lj^vYFSHJEa#YsHOW|1C}fKS%ouu2BQU2h|@i*i`G|O^|5CE%-KEd3UNX z?#V|h>--j~CM!#WGnQ_h0o_>PL?iqPJ&TM6T{>J1H&m&We-?`y=^2LoCDnR=BmLVV z`wP&Z`dhSlt$6MBV zCtOWg9abYS=%cOrHnfK7emF+URl2nc^!JyJq&Vgww4KnT77ggMgaK{83Ge_+j}K^P z3${Q*v8jpvqhUWU*5qKn+YF_UmJLxU)F82+6IJs1v*5Uu)zpnMYSw2en_{Eja5G8w5&D$I@*h3bqtB$ zn(F)dIS+;@)`$lbvI5qB)EYJetdyjhYoS`hVz*Lh&H_(xx?DKOn+P&pyGxAC(_0oMh1De?ldapsX}HEzIF3u1?o3C#^Bj`BHC$b< z2M{j);G-Haj%2!BhJv6F7ciz$I5lis0`ZGXuTPN|96_WE6rJN#=?{S4M}JM0%*B#1q>__x<;%3t&@np3bjfA z-GfZCuZ#(ye<4NVR8IBrLnx|Rb;3%yQana6iKV$hG%CHI&N|*+qJap*0XKiu9qi`zqNwswH8nF2Yzh9nE%6 zhLr->2Z3&AyyytglEuK5I_wSv!f9!BV%KBM;uvCKONf)Lzs0XDF^)bfy0$`{XTDIR*uQk|8`$i0e zzrCGfz!Zmr`j`a0MmbvQY!Dl<+eKAdu&ws0IE#Pn|B9S;db1m> z2&|UJhlAwyiqk7FHMiOucxnufhvIPLY(8O}o> zfMGRs=xD+16{1}S9M6?v1pd{Q$%@W#mhcs|7K0892NNo)EmC@6Dv0WJVCE_B30z`> zFN8F#L}G(I0BKl>1WsEJ5`)_7-3)79VlD2$T8wO1g_Dvq3&ieLEFMDx}35mhyoCS3Ww7tlC?w3khI2R|XKzcG3qmq1+}x$)*E@T@L2TXvyq} zVtnUVePLQs^#uTD6VNF8G@f3ds%Te>^_}(B%78Hac*~|;B2I(GWSOlsTGZ{LwdGiDN+sYb*1VbsfnbOSXQi$Hte3_bTMw~^Z@yr!lW~abmAn|6}BvKYqBf4jt*i) zSN+Nitrf5%WxHUYLG>sS)w_Yquv&;t_}9r7W4qx{hN-8Uo-tf&1~P*{Kf21OevIIT z;%^z`P??)RT0U@CH^sr!q~sFnRzsXX{Z5vs-d!JV*!9Ht?pP1(ST{+X@v_ws<9p&( zY%Q!uo;cB6hp)s{6Dzyw?Lu=Nn*Al8J<25;p$heHm^#^cQEBWMjI>c+ia-6Y}z!5c(j)nN&AU$3! z>Z~{gV!gDVv0jbDww_qRQ?Ys#WY{M_Y!;|X7(v!I;(}fX9v74Guk)2^sUu=@FKj6c z;IJnoRKfWS(r&|I$%&g<>M*296C@P>Q%I9?2`%-msNWm&IVgJe)`w&{A1OJ!&o?7M z_Cv7_Iqd_nyEjfgd&KG9dWQ__9Y*I%qH7;yU|Y10eqBEeCo{~q4#fANaC{`a2IZP!@$>dtu^#vg z=eZC}^dN(FQv=(6RvfYPCF(_Z>moDz>hR)Xbw6L;*M9;|D#14<5>Isqryq#mk&e6< zmqsq{ucxIM&i=@?m*`;~Ccv{pa5mg6It|rV8P;~OZzviTX7!=^0K<+5s}#G^th2rdReV$>@751h2u;k4+PieEop-c6N^uxMcTrff}Y$C5%(?Y?Y=D@q|GO05` z15o+|v2cXmnk4M0!U2hn@`?@9&`)#170auk1eB;3NoY4h$z(G^T7OD&t{06)>bIzT zuMx{e>g8?a2@22YhAJHn<2k<EG`(Ok5uJ=jlfYdE_6oe?I}$g zEY6NX;|>&^E`z`J1)}US{Z_;3C5~N&ey_Wzez^`eXS<44m!t3OB&J-BzO%hpe>reY zp=fl4egk}aO5A@1EZc4LZ9Uz_aB7HSS73uR6e*)oCM0H!2G*(t(?WHqHq9So>YC!v zXnl*}?1i%ljn(Tv4vNiVaCH144vo?OLy7j8$ds{qa-A9z&HQp*Y(@*fTEMM;$zuQOpi5Fzh81pFj7Z?6Ep zx|_FzxZqao5X;5nTlEDQ7Az@cox!a`Ttnr0K&0G;I`4;TI4)?pOE$UB&%-Kn1n>j2 zd&8+RMoPj5QsqL6WbQ$ku&dlwIkfrJkcMpzb%HiuEc?nF@#<|5oy#BEELyx!pr?R4 zBh{zrKk9mc_QH^Hk)00c(&CKDE6*Ke?b^2v>7G*Q_%-WM-SeRG~Qh-a3nfhIg zapjS!?n6TZl*=hVVKwXCb+-pH|BnVkI2&+xGmgX?xW%>6O1wc_ z+_~H@ZeNDt(aOT&Ra!%~GnK<0?iaF#QDr}o+LmE=WiM3X zE#sxt2KdFD3n*hD;J&PeyAIfPcq>L3*y-aJ!aME)?#nve6;GalnzEapx4Kd$SFOOC zznzp2Zr?^*v7=uIIx4n*xBg*w#o8!w+NF?r;>{Fq_i4L!_@V zx+KXlejc;T5Jju?G;!h)y-g7w{qW}Mz*5^ckk z$`&-NH&DK~aJygJhNZo?`K(t7e~H0yo#Aot){BJ0g56sgJjdX}Hxs^*!JlF9r$y=R z=qRwCq%OH-gwe5xJPxd`y}9CT4_B_D)<~m^nnoUB_=i0_uDu_^Qg$h_moxk_h7U_V z&tAmv54w5LW*0OD6@Na1-|xX61YDJVE`#F=!_y>MxhQQ9MjMag*t%O-&*+(A=u>)e zku}q$K?2(CE)6mb?zogbg^uP{hIh~M#7|W`PVjNZMEY@pkC%}6>lKgFx|g5G@Ds$E zL%OPwHXdsPcTBG&CE6Ij66XrP68mz+!|szeQztrEllPQ`w1+ExD8omm=tO?7;s-H& zbWTp>2PnQj!$+s%MBWmcpVGVQj_3moYFIr`ytuH~FAeWd^O+phgc5Jh;O!W^E#WO0 zyokXI3D0NnJOry2Z5H!dgpQwihC z`c8Tb{REl?e86$P4(*s<2W){6{{_Q;&hUp7{|Un%V)z4!|Cr%FV)!ai_HxW;ysv!5 zd!$6edDG)~oLzn;us0?vrM?!frxcHYytn+1G5lJ^uVVO>4F5>w7F?PLY7V${B%RVyzqGx?FKsO*d?ABBz~BoA zpU2=841O=+whJ%C@nO$#;WEvdEf&87jV^`6Phj|K+`Kq-2r5JPI0hfO;{e;9~~+RcIIcRXBqbKY-y&7{0&aEr##I@VzhqQfAk>quC9+E9uZW z`*k=S{W|Opif_yC7_G+FLS(*HiZ5jN<_uq;_*{mM2CuYiApJRtZ_Mz~xHXZ_QhX-E z*JH|47yT?QOU}QB%My6(4u?fGzqIWRQK=uK5*}o5cZ5nfY-OSL^9+8@UpDI>!vA3K zvkd+_;lD8W&kX((;Xg3=Ne2I(@NXFWYX(0~_?HZRgu%Z6&l+_YpGt>u4SeZc4bcOB zCDwkw5^W#m32~1*G$qr2!0>w&?+#6ge~00BEB-B}Ja01m8;ak~@b1`E<_ULasQfQ6 ze1y^eB5krzfwxGPw3$>`&-ztp8~rMrrxpJc!#~OJ>lDA1;ny(yD#g1)SE{Z%bd_mv zuwI(LTF&TqhpxmgRs14`cZaUTFI4<|hIhxV#LrXwT!y~~(md|au|@hZ=*X6wGVT_e zccTx!OSF3*`lUA0r9?8$43`p_W=~iAtqfnr@KY5(h2d{t`0Eva9m8MC@Dmh2p5d=# z_;I3iFRqiWp!!>vqkM7U2*0>}sbAb0PWTW8zl6aD6F!i^-EpflE}Y+!h3UuOeHrZ* z;XN6=2ZMJfybFWl3>@z+RG3b**FnwRj?AUCMIGSA+|eru6#12Cg_6&=^Zl9P>3LVC z;)7R>W}@{Ty+OX+gyFNjeB0L)p9e1`NL*D@dFs1)@!$`zu%haO7`%=fm!kv%SnMrU z4Xl2qUX5xm4+`qx=q|BTsU`cxouJ|ke|DPA@aJifL<;{_BlJ&FqW$hy;+*j-v42(k zPYmylP{|gj6#qTLf6M4Uq4?trf0W_BQv4ST{~5y{R=hiUm1T7fGCV}DazuZmM)Zd$ zUtIXUKRbJmU)*|+@V6O!7lZF4dij@rwi>dQ~~3Em0ySTD5QCu2%N_teg(q|hObn-J8q@& zxZ_rtrY%L`TU$37pT*)ARJ&?z{g$wA!ly~~BmOB#l( zaT5?eQ{)^3lUdUl9%EK7f2-oBGW^X9KSlA^GrT)=rSe>-_z4W}4qb^KPxAm(aGXg0 z6qgNSNQrj2Uy0+6P^FwbQt`tXz7*0tMzDt}elWvd#PEX@KY-ycVEF!uw-~-R!}n5r zcZToE@WqPn#PA&$zCHF!SeCRV`&z9~zPPZ3U)*l)7q-Vb z_?7sxZeBdN8d`#i=Z;?q|Jj3|isSD1mGF}ue8`%Z_HW=)d|5&G*B<;iz}r$`zGUzt zZd@*6zM#HkeTuft)IT9pSqJ>$+J3*d^ReRHk*h3%_5s7cuXuOlO8h$v|F+`aV#@O- z!|zc1c7}IHt};*O6{^1WlI&XI;iGWdy~RPjO}4hxufcgfnxi;o#NLr{Az|@rT9k}{vn26q4?ztA7=QaieJR=|6%xril5K$ z_c8oD#m{AUoPE8sfGu)Pf)~*4cJVoK7RKo}&dW6WPKs~|m?6?n!&GHWM&qy1{EE8J?t>aUz8?JC(|V6&y3`XZpQ(4J zCbo)!jQc)`Xc(z_ng^D4-ozlgMa8RoK;2m9tMAp!QUkuLs!pmvUf80TZB6d{yKwi zmm#67hy5~nuUogqyw?kUE!H--7O{70%)e}6aCg|6Bid|()qPra_1?m z5-c4bXVgE&@M|TXXJh#4F&VyRCD8+kA@z)CaWH!I;+1Y(7H2u#ET{4=A*Z*9iYO0C zdvs_I_;on&>K5r`=K1r~Dj0sQ;%yh7F3U5=#mhA3Zn5}xbgCG>#>+$e9SkoP{fl}hLh-{Gekj8aQT)XWKZxN6D!zo_`!jq$#rI+OUJMUU*8Pr7uPfQtDn|L@!j67% z+a0jVX0h56-kQN%`E#>c65gD_3m80~@TLr&!{AK_$Jo`=zw9gqZ;;Tx@X0*vUvMfI z$3T(o4qK_Rwfsu7nu<^JXKJM~e2U^Rdi502G8x{Gd_KPM%*X%Z;YFV&MwcR4RvV*N zum0b?{A%Fa5|81lm;cGbR8|S#Wi=#DzkBpDgH}_KjP2M z`CRdzGW`D-{*dA^g7s8K+sE)9DgFb7-^=iO6#ovx?`HU26pF~oYi}wawu6*t?$DJC z^r~Np{fgpW@@MM2$nY;Hehb5IX828t-^lP!GyDd{Kgsax7#<$g!#?AzRz3#qxT24F z*e`Co!&hk%3vRd~zKp@c{@kpkboWHc+7HSsMTcFmI-!*Lel^zpZZ!gr1S@{w;%_VDN7UKg!@=G5D7dzV*;QBeQCUQMgOK?G9XJGg=2d(qeV{n7438 zuEc-n<9b@`mX88Yl`8gep?lhdN zZPIb+@DelWiZ=PhwdXwIc$;ve;x{n-dWL^e@sBgSJ8q?swnp)fGQ2x(CH`SLBr1Ps zg8G2HoRnxw{Ysq0{zBqg&d>O=UHs{P(4ezjyiBw27G;gG zJa;nuOoqQ*@zWUoR))Vt@i#O46o$W1ZjexUCyA;|bYIt@j^e^={3>jB;3~ze@q~|M z@G<@z;gv8_`zQu?hpnW21mUF&K9tcugz$?Pd=P^VB>Vyf@6X`<#OZ8sNbuw<8cOSh z(h!1m^NU+u{o-1&;@vST)w2V`w^zJ7XeGWC!?#qtJ8C7qfZ_8M?+#mu&tZ6Y(Des6 zkVim6G(x7B3SFhh$^beZ#YjM_=hpzQUXT{E(iy%M!`D!J8pEeDe6r#L3~w^Lu6TFQ zO67M4tuoE|ix!9koTYAH|3*r*U;LRm?#NWi*{2o%1H*ssFQomQ;@zPu73es_A5*+L zbS3@^hW}jg?$DL^PZ<7?;`hTfPop~@GyF%2|A67&XLxv1O*Ss4b}N7MHp&+lzUddY z-|&lDI|zS`!Cz(YR|tpmfh*x*H;xmVwiGJ`e(yo1IR9bzg^I`M z)ze|D`xt&6oyDdC+#~&rZj15F;VBk;b9aJ{BeFcRTsmZ$Bd%(Wj>#Rj5h6#jQ?+w`1_O4Bm$DA_gyH@D_yUF?cS6HzPco!5cGpBf{%5xI18#)`l08 z!eib&^g7Z|;^!a0VNjQ;;a6Z)_v9yro`QX3;@u%DDNpk9o8r7XW+h(p^0j`{yR;?! z?x2blJ!D?mdzo1QEYvnZXaW5iuXg~UOI6o-p^R;D~#6u*h#pJn)sir>KSPcb~aNTNOZn8%cl zS&Q<;g^&8hZFh_+t7APvxM1)~e{R-t!j~}kVg_GC_yY{SfWhYzUcun^GWcA&(OZR6 zs69ut>w_W9?8o!G{&#)(C#!s=<%RbUGSjD@FSO&n~-fH{yhDGdFgvX@d=1fQYs^$#a2%E{x7 zvI2GkSzr9pgtt36bs0QFcshgEWbhhiv7vX0a{5J+a zL-@}Oj?=9)c!D8+B>W_U<8152zZE&X(8(SrCu1E0@#4ZGerf#Lg;(18T+;tz=ua5> zkfir9G)}Rel35=~dM`uc1nZ^u(2gW^rrpY8z6~nCW4`IvVZGtkq3t02H3ol`!Cyf< z&vr+rwozsSJO!;%{X5>luEs z;wLiv1ctvx@mDeYIEEjq_$wIxGKL?e_)8fcXW96mls2$S6@LlC<238#FH-!443G1y zmxnjfUWm@ik}YUheNeu*u!mpV?(P@2x)F{6YJ7Z9$vZK4N5U~c_2M|cdI|?$HI#+H z0M(1*EbGOa6Q0Z9IM;gdrdahJdSmLl{c6BV1gPZhxKyTDnSKRY2IdFOv5r4Ot2V=@ zOFqxW5YLAQFbJ&t35>c!7{@a)y#9ZCD24E~23m)`Mg z!g#lSMVn>nKa&A0j88pTYNz}_$L|u3B^Cg@M{=;wc=MYJWj<4<4Q(@4=KKq;c+hZ@?l{Q zM<2OJ`N#)RzPNCKU)-MW7q{*w9D`I(r?lrX_&xAGZqPp4g>RMxzuSe&G;0>&GZ`G` z;CNjKpF#Mo431N<7l*eRV_~K+IL^Rc{073WWAKR#el3MuRJrlOz7k{ft564cRSZ$% z_M*~{_AAlg)e*qw*&`W#gqs(8H$Y`5yP*s|#DlkaI))Eo@PQuu!8qQZ!TWjeQ-HUn z!t`SBo^D(^;U4sS2pOXo4VEc3K?4<89sC-!_I?dcTe7azis4%_e4*kC7(Soja}}S% z@J$%LvEs8Bz5&C-D-ZFNpV9*O>R{|Lb{$fp)$%KGYWS7d)fJ!0@W~9Hq0}<0hi1sftdc%PUrE-LcUL_*YAgIIoJz%G6zk=eGW=r2V;Jk@ z7c%?;#bX@n<>xW{z4Bm#zFC}l5$;c}k(22hQFaSPp0iv!N8~;b6ZC{LFE}Ad^zoMT8Gv@Dc`x*cG)~4Bm&qdlTNB!MibdSHe3nct-~BKzJJl zZ_VJX2yemQ%^AFa@Ma9&l)-aomZXl`NSwYN7gh~X2P~{Gl#LG>vV4YLiB?bXwHZF0 z;cF?r8pEeCe5&Gu3?E>4Q}Gzgddq)~;s23*zUQS9RNu26UMyM-_e;oLzcBiL_V9QY z_b1vfB!xeqku&x0sf^YMzYgsizYf?lmTh8v#qeJ;{1=ihv<@@;rwlKq!U!qyyj((- z*zoKwCpJ(_@3FAuJNaG4#Ga;3DUNb=kdcv;+KXHtiYHR(OI;$CemEs>^ z_=gyNh2obpJWj~*rHw434dU@!Ct@#tJ>e4> z9Oq##4)2#h?N>24PQzXt-iC-DfgoF4A<}KwQ@;mZM^Qv67|nW2U|s4_Bfi)GHTS^S;AvX=b!bI?9q^_J;`13kkKvmsz6rx;Gkhb(V@&I56RkeOXDB|z z@O2nIopuJK;j|jE>lk)5Qlh2!l{iU$CGe&RX#~%kCZtYBWBBum|C`bOC&T}t_}>`* zSBC#Z@uwO76vO|Z`0p4VXJpTKv%gXNF^0z(*~@<^*4&Mr>@%`2ylO%Y??ZlZ`=DRk zIzaeG4E`a5R}sF4!QW%>cM0Fc;5!-oEy5iJf1Sa%C%Pth*95vIeAk2;+8ES&y0zzB z6N~>5A8d`e8jNba{BxdcMcZw#CL>pXVXc>6@8QuEJcYG_HSXgK{}{usm3+SEZ4)w2 z&)X)T{6p)-T$y0>S9n3QV&Q-ka z;%yn*&vE4`)8KUzRrAnQV$d7cPy8JWf4kx_==JiqG5jrxznS50V)z^B91;~^vdDP= zxA?9jC7$<9PzA2`D}nb-$VzGB{Fypq7=EPZY{?>r)(m4 z;e^C7wDsZz44zMTQwGmr@Fs*eWbiBoZ$NlG2CvKDA;N1hcufYcA>U6gt8K%36S4Pd zDX0S6Qoygn^R@}<_3-I#;PX6hn;=vEjT0uko8pFe%GIw^$y`X8T>5&4-$6n=Gv z{O2RG=`?s1g>;V?zAO`deg@9moPP?P-A#TC+7!PA=LVUf zHHpEmWAJMUzna0vGx(K+k74l941PJ`7}Lhvj%qrB!G{w*l);BE_+Y{ZG5A0RAAs8( zur7tq*FA$iv9A=@di%v4+&uQQq}_w?t_^t6sVWLsyq{ z3PUF|G<*eKetlFfZs9G7SQ$+R6)+&Q?JvSHRP_|l`i;TQ5RRd$7eCG5 zKN60isu%x`!M`Q^ID;Q!@S}u(!Qh`W_-BNF!r+G({2**5(6Yh$Sh^nlL#e>RDAiLN z?R~!j=RLxAGx*yKzLRi_QN7xCF!<|)V~Fa-Ut#e768<8Czrf&IX^3d>r8?;yo|EF* zGk$SrgJ0ZUPxuoI{y2j_M)+z5f0V&j68;c_3kI(wJj~!r7<{qv4)75==^Y*b@#4b! z{L=P3zqC~$>2ij)U396OBj@0U>pHZ-|2ucNWaM@Ev}}CDlFS_mG8m%9Tc2d6G5Oq< zAcJwLSLS9$<|esZNg<`zhsB4EQBT}65CTR@y4J79n&8$WJ|76CBm7DRALqe)4}$oT z@XHzeGB=J^(?-Q>W@EVOshNCy)?-1fRNhdbnpuNo%`)_h;)<*b{fe{!{>+^V5@h-^ zG8QA#J3*!gBjXNQr7_^m#IbH-b!KEbG5K^zkijU{V;K2Rt;aA+ODm5_Z&++ z#;jhxn&REFEb&PUA5^>n*E~k_+?ka+;2Qu`ew=N+JjSfv^8BHAoNm4RuMGc-uwTX1 z%a3F)3%;xVGK?~uPd(!Hw|;T!1mQTJdhw$S{uSXkpL%hORy~!0+p@ASIG=j)gA9Iv z@Q)ZAqgk)^D#G_LI7YKx{9VF#G5Agfe~WO3!7-ZkYTr)y%M6autQUWY@NEo^(X1EW zLhoy$F7=#jYSCev-b9Rh*=SH?*=Djl`8LkG-tts>dAKPX<#FQm@)*-5nnBdcP<)a70K;QUo55yso4S=`tg-~}g{2dH07QKxj=`;qvjltm?0HEIc4FD<* zhPCl3QE_fm{A5P|B!-`;_-hy*16!~DtLXR&6=1BeK1L@D-vGcuYB=S3Z02D6>QQ3D zHvl9*jNvhW_44oy0Lfp>@Pim0z5yWl5{B>3@cks8q4i<-UZTZ&Mh`LPW207)){Oy* z8HI2Yb~k$64rCyF>p!%*=O(PQq30&7OvAVSBi`Jfn_b|~&4Rc7Bi@w3a~K=~S;QMM zcou^yRh#WS%#C0W3aiHwAZMWZ3V&${ycwl zMVfmyBmoFx-!q!%t;FW{j1C5TTpjvjrv8^$ycfsJS$`Si*I+$maNxyGlK+|EfAW_Z z=2GHMGW_=p|E=P`X87X_e^l{D82$@}|4i}!WB5-P{-Ef&7dLbEsp0z($;*So-r?&G zRmtk`PJgtQ_KrV4=WTy}_AbT0$?!WE-ckH(4DXIssXQ+$KEm)XF#5NNvML;%o7L!q z*ZJ>-0r8Aq+8)2D09qZk9s*sdzUqLf11wII~_I4(iGD0Ye#$Und6dhl8zDlnLVg z{l;{vv^AzUN!P+gWo9T?J~lMp(89rhcAKVY#ZTmE;na#&;lL<)O>F+sXxJ+&j98ME zBuLjN5W5r7vf^pZ2q_v2-Xc}TSnE=8?*XG*Ln&y%G+I6u1asno+7NN*fYH6kx=39s zNVo>msLUvVcP9T8X;WD$YCoQ3w7G2Q^I;m=6ZskRk`O3tLr?DO#ZDky*%FMxAO z?EBOx$aelhnNYRh-Lbra(Y&-jMg7ANVd8$JG*E|lrL;3*GF&UN!0F3ty%Vm%hNU!6 z0uTAE`O;`9em`u~%Fw{`OQEiDA?I7s=rf~tk#k%LCn&TbrcxG+8{()a`x@SK@r7_c z!`<1>#i7rPW*N?>N<2X|-qrgYRNDvCwF~0eTKi+FO`3tzi^3+#GAMr4zz!#n&1&aN; z*!_jkFT;5m#IW;T6wk)~ugE!q%uAx{5iAZwILOH2WXCyYi>zLj^BiUEy>2}#jvX<2 zXIUE(t|wOKDOp`ZTPJ3IX%rYXgeqSe-FrKa0zWDoEH91cWv_(amqPlTkfyqq^$7gN z{+-ah>;>`9#V>%12nOe(xKu0CE%^N9=SJJ6)gd=>6_h-Na*>v*F7ff- z!mG(zd~LLDND4=~$|2KWDGud;4;jiFOU1^op)g0JyB-ob{I#*bXrtwO6vH=zC6Ej0 zaTAe-I%3(kMnMOsNeG%W6kJ{>94t?fW|G(OltQez>%{BiG!iGjg)U>)r|acpDw4?* zD}OMG#LeFs>4qH=_IJ@DTD2pazccPJGVm#`@)XF5yq6Tyz7Yd|FnVWbN0mzQc2vwS zQDyHA4s1%uR-@h^ui1x{E^@h4gy;ZF5r2OR3r+i=uud5Rvha18;QfxEIkb<%)q@%~ zStt;kFV>ubUZ;H^b^~YF?~3#vvDd#NTKtI4XQ#?Hp_RJ;UKF;Q1`oVYwnjq6wvm9WP`wKT{#?(}6A&P9>BzZp*&8BSYO zC~}bUqTNPRorO{E6omrib;H4Oc`XDpppe(CLXr1}@lgi8XotEu&W?1=$r9E7G`hFZ zaCVg4KjB(E)xGjSCyhALS658?(->Za?-!8W4~9?Tb-PBm8jOzz9%(LzR-oiBar#eV zh+!uS>o0JARxqwN!Q)U}Okw|JTx2-s#Gb#5tSo$IfXoBSK*N)`zbgMmmA{G7zcH5i zRZRIC2l`Ky=b#7JKMLn>BipclklMO9C&CqJr<5l0e^iriqS|qggvmcD%h<;$2RH{J zbNF2B`3F1Yr!oiaf1>_5$N^sMisj9+K874%e8{pUkL7(sEIv<>7W}DwE);ABwr!`098H71f3-Mr9xMBb`1`!kJlHA(i<(N^ zY+oyw7H#SmLeOfr5R7`abSB9ahFBLe`zGUhv7%yuI2khM>U~66U2`@3Nv~&4)h`rw zJ@c~K+lzMslk~)!@^A9;{o+JDv$_6CBt64yqZ{^x!pbxYGZ6~zTug9DCBYSs2|E+a z{A6TBrkSkg_YMck;H_S=p!;M&l1h4og6T#W{!zN~kytY`kP|sp-+VwHrfP@7SC&3v zK3SogPn76p?k+8YGg(DNAe3}Oo1~dqNZ0?#EvOCaz2}YGoV7-AelGZyoriOq!T-^P z1}l<;lV#@CxF!@VkE&cRPG^|~%_?f-wpa_IJG9&)!0VxCNt@hOQPC3k9&dr!EG=2d zIcvnV4b2<%`l70#Sugnns889wOPp+Ic4$*^U2fjmfYep6Hc(t4vqt@aaImkQTl9PY z^aiL%JDZEajm&JL;yN(}{$@cr;F{#x0GG2iNpTItx<=-h+ATxD6sUDlw7r{&^v33; zwM)Xm%|<9GS~`2KnAX@lFe+LS-N?m~NP`4-U`ay3z6R7ZPz>DF`Npi)SY_nyz8>03u-Y+XS80-wH#v6db-@cl(d$jU-V94Xh?R-|8%{CiV zLn&B?41-KX!zSi5*l$_f#4IYHh5#d}_Jb-wBjmQ(fo&Q!S}Sp+iP^SZy9srJ>2sf+ zuT8i&Er}GK`9|dBn3w8BVrGtcyZ((hons1k9`OFAW(59N&CH?jXK^#LbtV=kY3HsG z7&DxNGwykqah;Z8Uo#WFNG~?#LbGV?#hzSqEBv(cq-0f|*}9>UgkB333M5E_&u`S! zgh%d+%oNcx-^}m$HD?WaoiS47P8R(22orQnVR# zDCn4~L){@Z7MK?mMP+vAahZyRXlvDmf#_fmIn7}#&k|jmo6qP^M2tHvm=7a#kUsaxooa-RoGnUQf}`7s&0le!*xl01)dS*WOS4D4*`cHy zbZOdD%}K_BL6?{y0wOjP7KsOmM|R8T1m{+M7+K;pUY* z5INl5tgZJzeBRDQ;ffSBFIaELG9>L>6oRyH5FCk|FYK3TbeEJwNajJ&qN6!1x$19R zo!%C)JDN3g`eofBmUS|JP?xR|_jfk0P(Q|s6P?X*^urk`26r(Zr62ai;&d1D!Z`;c z$FW2epM-<9Ax>k>wByoWBvrsNIT=cns!_T$= z7nQ+TQSrB^v;(1vInhL|EFudM+=oI+!Q_}Q6hgXQSwQQin1Wd0v?*~Gnj0s4$=Wwi zdIjvJCRWC|My7X6iq;0~P6Cs{^8;E;S*+CBbxP4;F}bUG&1f0>$XE#HdLf$ycmK;% zL^^sC=gzqoK&-TXnkE*@#d}K_FC%kCfL
    y-z9}82LvTR9r^99oI!2G2Jps~c$K{;cJ5Had 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 diff --git a/ethereum/light-client/Cargo.toml b/ethereum/light-client/Cargo.toml index 4ec4c8f0..4d9ef727 100644 --- a/ethereum/light-client/Cargo.toml +++ b/ethereum/light-client/Cargo.toml @@ -14,6 +14,7 @@ hex = { workspace = true } log = { workspace = true } reqwest = { workspace = true, features = ["json"] } serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["full"] } # LC crates @@ -22,9 +23,6 @@ ethereum-programs = { path = "../ethereum-programs" } # Sphinx crates sphinx-sdk = { workspace = true } -[dev-dependencies] -serde_json = { workspace = true } - [[bin]] name = "client" path = "src/bin/client.rs" diff --git a/ethereum/light-client/src/bin/client.rs b/ethereum/light-client/src/bin/client.rs index 85577b03..f5935437 100644 --- a/ethereum/light-client/src/bin/client.rs +++ b/ethereum/light-client/src/bin/client.rs @@ -1,11 +1,14 @@ // Copyright (c) Yatima, Inc. // SPDX-License-Identifier: Apache-2.0 +use anyhow::Result; use clap::Parser; -use ethereum_lc_core::merkle::Merkleized; +use ethereum_lc::client::Client; +use ethereum_lc::proofs::ProvingMode; use ethereum_lc_core::types::store::LightClientStore; use ethereum_lc_core::types::utils::calc_sync_period; use log::info; +use std::sync::Arc; /// The maximum number of light client updates that can be requested. /// @@ -26,6 +29,15 @@ struct Cli { /// It is recommended to use https://www.lightclientdata.org #[arg(short, long)] beacon_node_address: String, + + /// The address of the proof server + #[arg(short, long)] + proof_server_address: String, +} + +pub struct ClientState { + client: Client, + store: LightClientStore, } #[tokio::main] @@ -33,15 +45,36 @@ async fn main() { let Cli { checkpoint_provider_address, beacon_node_address, + proof_server_address, .. } = Cli::parse(); // Initialize the logger. env_logger::init(); + let checkpoint_provider_address = Arc::new(checkpoint_provider_address); + let beacon_node_address = Arc::new(beacon_node_address); + let proof_server_address = Arc::new(proof_server_address); + + let _state = initialize_light_client( + checkpoint_provider_address, + beacon_node_address, + proof_server_address, + ) + .await; +} + +async fn initialize_light_client( + checkpoint_provider_address: Arc, + beacon_node_address: Arc, + proof_server_address: Arc, +) -> Result { // Instantiate client. - let client = - ethereum_lc::client::Client::new(&checkpoint_provider_address, &beacon_node_address); + let client = Client::new( + &checkpoint_provider_address, + &beacon_node_address, + &proof_server_address, + ); info!("Fetching latest state checkpoint and bootstrap data..."); @@ -79,15 +112,18 @@ async fn main() { .try_into() .expect("Failed to convert checkpoint bytes to Bytes32"); - let mut store = LightClientStore::initialize(trusted_block_root, &bootstrap) + let store = LightClientStore::initialize(trusted_block_root, &bootstrap) .expect("Could not initialize the store based on bootstrap data"); + let mut client_state = ClientState { client, store }; + info!("Fetching updates..."); // Fetch updates let sync_period = calc_sync_period(bootstrap.header().beacon().slot()); - let update_response = client + let update_response = client_state + .client .get_update_data(sync_period, MAX_REQUEST_LIGHT_CLIENT_UPDATES) .await .expect("Failed to fetch update data"); @@ -109,35 +145,24 @@ async fn main() { info!("Sync period changed, updating store..."); } - store + let proof = client_state + .client + .prove_committee_change(ProvingMode::STARK, &client_state.store, update.update()) + .await + .expect("Failed to prove committee change"); + + client_state + .client + .verify_committee_change(proof.clone()) + .await + .expect("Failed to prove committee change"); + + // TODO this is redundant, to simplify + client_state + .store .process_light_client_update(update.update()) - .expect("Failed to process update"); - - assert_eq!( - store - .next_sync_committee() - .clone() - .unwrap() - .hash_tree_root() - .unwrap(), - update - .update() - .next_sync_committee() - .hash_tree_root() - .unwrap() - ); - - if calc_sync_period(bootstrap.header().beacon().slot()) - != calc_sync_period(update.update().attested_header().beacon().slot()) - { - assert_eq!( - store.finalized_header().hash_tree_root().unwrap(), - update.update().finalized_header().hash_tree_root().unwrap() - ); - assert_eq!( - store.optimistic_header().hash_tree_root().unwrap(), - update.update().finalized_header().hash_tree_root().unwrap() - ) - } + .unwrap() } + + Ok(client_state) } diff --git a/ethereum/light-client/src/bin/server_primary.rs b/ethereum/light-client/src/bin/server_primary.rs index 331f974e..293619ce 100644 --- a/ethereum/light-client/src/bin/server_primary.rs +++ b/ethereum/light-client/src/bin/server_primary.rs @@ -1,5 +1,77 @@ // Copyright (c) Yatima, Inc. // SPDX-License-Identifier: Apache-2.0 -#[allow(clippy::missing_const_for_fn)] -fn main() {} +use anyhow::{Error, Result}; +use clap::Parser; +use ethereum_lc::proofs::committee_change::CommitteeChangeProver; +use ethereum_lc::proofs::Prover; +use ethereum_lc::types::network::Request; +use ethereum_lc::utils::{read_bytes, write_bytes}; +use log::{error, info}; +use std::sync::Arc; +use tokio::net::TcpListener; +use tokio::task::spawn_blocking; + +#[derive(Parser)] +struct Cli { + /// Address of this server. E.g. 127.0.0.1:1234 + #[arg(short, long)] + addr: String, + + /// Address of the secondary server. E.g. 127.0.0.1:4321 + #[arg(long)] + snd_addr: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + let Cli { addr, .. } = Cli::parse(); + + env_logger::init(); + + let listener = TcpListener::bind(addr).await?; + info!("Server is running on {}", listener.local_addr()?); + + let committee_prover = Arc::new(CommitteeChangeProver::new()); + + loop { + let (mut client_stream, _) = listener.accept().await?; + info!("Received a connection"); + + let committee_prover = committee_prover.clone(); + + tokio::spawn(async move { + info!("Awaiting request"); + let request_bytes = read_bytes(&mut client_stream).await?; + info!("Request received"); + + info!("Deserializing request"); + match Request::from_bytes(&request_bytes) { + Ok(request) => match request { + Request::ProveCommitteeChange(boxed) => { + info!("Start proving"); + let proof_handle = spawn_blocking(move || { + let (proving_mode, inputs) = boxed.as_ref(); + committee_prover.prove(inputs.clone(), proving_mode.clone()) + }); + let proof = proof_handle.await??; + info!("Proof generated. Serializing"); + let proof_bytes = proof.to_bytes()?; + info!("Sending proof"); + write_bytes(&mut client_stream, &proof_bytes).await?; + info!("Proof sent"); + } + Request::VerifyCommitteeChange(proof) => { + write_bytes( + &mut client_stream, + &[u8::from(committee_prover.verify(&proof).is_ok())], + ) + .await?; + } + }, + Err(err) => error!("Failed to deserialize request object: {err}"), + } + Ok::<(), Error>(()) + }); + } +} diff --git a/ethereum/light-client/src/client/mod.rs b/ethereum/light-client/src/client/mod.rs index e310370f..4d27c282 100644 --- a/ethereum/light-client/src/client/mod.rs +++ b/ethereum/light-client/src/client/mod.rs @@ -15,19 +15,25 @@ use crate::client::beacon::BeaconClient; use crate::client::checkpoint::CheckpointClient; use crate::client::error::ClientError; +use crate::client::proof_server::ProofServerClient; +use crate::proofs::{ProofType, ProvingMode}; use crate::types::beacon::update::UpdateResponse; use crate::types::checkpoint::Checkpoint; use ethereum_lc_core::types::bootstrap::Bootstrap; +use ethereum_lc_core::types::store::LightClientStore; +use ethereum_lc_core::types::update::Update; pub(crate) mod beacon; pub(crate) mod checkpoint; pub mod error; +mod proof_server; /// The client for the light client. It is the entrypoint for any needed remote call. #[derive(Debug, Clone)] pub struct Client { beacon_client: BeaconClient, checkpoint_client: CheckpointClient, + proof_server_client: ProofServerClient, } impl Client { @@ -41,10 +47,15 @@ impl Client { /// # Returns /// /// A new `Client`. - pub fn new(checkpoint_provider_address: &str, beacon_node_address: &str) -> Self { + pub fn new( + checkpoint_provider_address: &str, + beacon_node_address: &str, + proof_server_address: &str, + ) -> Self { Self { beacon_client: BeaconClient::new(beacon_node_address), checkpoint_client: CheckpointClient::new(checkpoint_provider_address), + proof_server_client: ProofServerClient::new(proof_server_address), } } @@ -83,6 +94,20 @@ impl Client { self.checkpoint_client.get_checkpoint(slot).await } + /// `get_update_data` makes an HTTP request to the Beacon Node API to get the update data. + /// + /// # Arguments + /// + /// * `sync_period` - The sync committee period. + /// * `max` - The maximum number of updates to fetch. Maxed at 128. + /// + /// # Returns + /// + /// The update data. + /// + /// # Errors + /// + /// Returns an error if the request fails or the response is not successful or properly formatted. pub async fn get_update_data( &self, sync_period: u64, @@ -90,4 +115,49 @@ impl Client { ) -> Result { self.beacon_client.get_update_data(sync_period, max).await } + + /// `prove_committee_change` makes a request to the Proof Server API to generate the proof of a committee change. + /// + /// # Arguments + /// + /// * `proving_mode` - The proving mode, either STARK or SNARK. + /// * `store` - The light client store. + /// * `update` - The update data. + /// + /// # Returns + /// + /// The proof of the committee change. + /// + /// # Errors + /// + /// Returns an error if the request fails or the response is not successful or properly formatted. + pub async fn prove_committee_change( + &self, + proving_mode: ProvingMode, + store: &LightClientStore, + update: &Update, + ) -> Result { + self.proof_server_client + .prove_committee_change(proving_mode, store, update) + .await + } + + /// `verify_committee_change` makes a request to the Proof Server API to verify the proof of a committee change. + /// + /// # Arguments + /// + /// * `proof` - The proof of the committee change. + /// + /// # Returns + /// + /// A boolean indicating whether the proof is valid. + /// + /// # Errors + /// + /// Returns an error if the request fails or the response is not successful or properly formatted. + pub async fn verify_committee_change(&self, proof: ProofType) -> Result { + self.proof_server_client + .verify_committee_change(proof) + .await + } } diff --git a/ethereum/light-client/src/client/proof_server.rs b/ethereum/light-client/src/client/proof_server.rs new file mode 100644 index 00000000..7202b2a5 --- /dev/null +++ b/ethereum/light-client/src/client/proof_server.rs @@ -0,0 +1,147 @@ +// Copyright (c) Yatima, Inc. +// SPDX-License-Identifier: Apache-2.0 + +//! # Proof Server client module +//! +//! This module contains the client to connect and query the Proof Server. It creates one-time TCP +//! connections to the Proof Server to generate and verify our proofs. + +use crate::client::error::ClientError; +use crate::proofs::committee_change::CommitteeChangeIn; +use crate::proofs::{ProofType, ProvingMode}; +use crate::types::network::Request; +use crate::utils::{read_bytes, write_bytes}; +use ethereum_lc_core::types::store::LightClientStore; +use ethereum_lc_core::types::update::Update; +use tokio::net::TcpStream; + +/// An internal client to handle communication with a Checkpoint Provider. +#[derive(Debug, Clone)] +pub(crate) struct ProofServerClient { + /// The address of the Proof Server. + address: String, +} + +impl ProofServerClient { + /// Create a new client with the given address. + /// + /// # Arguments + /// + /// * `proof_server_address` - The address of the Proof Server. + /// + /// # Returns + /// + /// A new `ProofServerClient`. + pub(crate) fn new(proof_server_address: &str) -> Self { + Self { + address: proof_server_address.to_string(), + } + } + + /// Prove a sync committee change by executing the [`LightClientStore::process_light_client_update`] + /// and proving its correct execution. + /// + /// # Arguments + /// + /// * `proving_mode` - The proving mode to use, either STARK or SNARK. + /// * `store` - The light client store. + /// * `update` - The update to process. + /// + /// # Returns + /// + /// A proof of the sync committee change. + pub(crate) async fn prove_committee_change( + &self, + proving_mode: ProvingMode, + store: &LightClientStore, + update: &Update, + ) -> Result { + let mut stream = + TcpStream::connect(&self.address) + .await + .map_err(|err| ClientError::Request { + endpoint: "ProofServer::ProveCommitteeChange".into(), + source: err.into(), + })?; + let inputs = CommitteeChangeIn::new(store.clone(), update.clone()); + let request = Request::ProveCommitteeChange(Box::new((proving_mode, inputs))); + + write_bytes( + &mut stream, + &request.to_bytes().map_err(|err| ClientError::Request { + endpoint: "ProofServer::ProveCommitteeChange".into(), + source: err.into(), + })?, + ) + .await + .map_err(|err| ClientError::Request { + endpoint: "prover".into(), + source: err.into(), + })?; + + let res = read_bytes(&mut stream) + .await + .map_err(|err| ClientError::Response { + endpoint: "ProofServer::ProveCommitteeChange".into(), + source: err.into(), + })?; + + ProofType::from_bytes(&res).map_err(|err| ClientError::Response { + endpoint: "ProofServer::ProveCommitteeChange".into(), + source: err.into(), + }) + } + + /// Verify a proof of a sync committee change. + /// + /// # Arguments + /// + /// * `proof` - The proof to verify. + /// + /// # Returns + /// + /// A boolean indicating whether the proof is valid. + pub(crate) async fn verify_committee_change( + &self, + proof: ProofType, + ) -> Result { + let mut stream = + TcpStream::connect(&self.address) + .await + .map_err(|err| ClientError::Request { + endpoint: "ProofServer::VerifyCommitteeChange".into(), + source: err.into(), + })?; + + let request = Request::VerifyCommitteeChange(proof); + + write_bytes( + &mut stream, + &request.to_bytes().map_err(|err| ClientError::Request { + endpoint: "ProofServer::VerifyCommitteeChange".into(), + source: err.into(), + })?, + ) + .await + .map_err(|err| ClientError::Request { + endpoint: "prover".into(), + source: err.into(), + })?; + + let res = read_bytes(&mut stream) + .await + .map_err(|err| ClientError::Response { + endpoint: "ProofServer::VerifyCommitteeChange".into(), + source: err.into(), + })?; + + if res.len() != 1 { + return Err(ClientError::Response { + endpoint: "ProofServer::VerifyCommitteeChange".into(), + source: "Invalid response length".into(), + }); + } + + Ok(res[0] == 1) + } +} diff --git a/ethereum/light-client/src/lib.rs b/ethereum/light-client/src/lib.rs index 41137c91..d19f8865 100644 --- a/ethereum/light-client/src/lib.rs +++ b/ethereum/light-client/src/lib.rs @@ -32,3 +32,4 @@ pub mod client; pub mod proofs; pub mod types; +pub mod utils; diff --git a/ethereum/light-client/src/proofs/committee_change.rs b/ethereum/light-client/src/proofs/committee_change.rs index bfabac83..3318c4e7 100644 --- a/ethereum/light-client/src/proofs/committee_change.rs +++ b/ethereum/light-client/src/proofs/committee_change.rs @@ -10,8 +10,11 @@ use crate::proofs::error::ProverError; use crate::proofs::{ProofType, Prover, ProvingMode}; use anyhow::Result; use ethereum_lc_core::crypto::hash::HashValue; +use ethereum_lc_core::deserialization_error; +use ethereum_lc_core::types::error::TypesError; use ethereum_lc_core::types::store::LightClientStore; use ethereum_lc_core::types::update::Update; +use ethereum_lc_core::types::utils::{extract_u32, OFFSET_BYTE_LENGTH}; use ethereum_programs::COMMITTEE_CHANGE_PROGRAM; use sphinx_sdk::{ProverClient, SphinxProvingKey, SphinxStdin, SphinxVerifyingKey}; @@ -42,7 +45,7 @@ impl CommitteeChangeProver { } /// The input for the sync committee change proof. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct CommitteeChangeIn { store: LightClientStore, update: Update, @@ -62,6 +65,57 @@ impl CommitteeChangeIn { pub const fn new(store: LightClientStore, update: Update) -> Self { Self { store, update } } + + /// Serialize the `CommitteeChangeIn` struct to SSZ bytes. + /// + /// # Returns + /// + /// A `Vec` containing the SSZ serialized `CommitteeChangeIn` struct. + pub fn to_ssz_bytes(&self) -> Result, TypesError> { + let mut bytes = vec![]; + + let store_offset: u32 = (OFFSET_BYTE_LENGTH * 2) as u32; + let store_bytes = self.store.to_ssz_bytes()?; + bytes.extend_from_slice(&store_offset.to_le_bytes()); + + let update_offset = store_offset + store_bytes.len() as u32; + let update_bytes = self.update.to_ssz_bytes()?; + bytes.extend_from_slice(&update_offset.to_le_bytes()); + + bytes.extend_from_slice(&store_bytes); + bytes.extend_from_slice(&update_bytes); + + Ok(bytes) + } + + /// Deserialize a `CommitteeChangeIn` struct from SSZ bytes. + /// + /// # Arguments + /// + /// * `bytes` - The SSZ encoded bytes. + /// + /// # Returns + /// + /// A `Result` containing either the deserialized `CommitteeChangeIn` struct or a `TypesError`. + pub fn from_ssz_bytes(bytes: &[u8]) -> Result { + let cursor = 0; + let (cursor, store_offset) = extract_u32("CommmitteeChangeIn", bytes, cursor)?; + let (cursor, update_offset) = extract_u32("CommmitteeChangeIn", bytes, cursor)?; + + // Deserialize the Light Client store + if cursor != store_offset as usize { + return Err(deserialization_error!( + "CommmitteeChangeIn", + "Invalid offset for store" + )); + } + let store = LightClientStore::from_ssz_bytes(&bytes[cursor..update_offset as usize])?; + + // Deserialize the Update + let update = Update::from_ssz_bytes(&bytes[update_offset as usize..])?; + + Ok(Self { store, update }) + } } /// The output for the sync committee change proof. @@ -115,8 +169,6 @@ impl Prover for CommitteeChangeProver { } fn prove(&self, inputs: Self::StdIn, mode: ProvingMode) -> Result { - sphinx_sdk::utils::setup_logger(); - let stdin = self.generate_sphinx_stdin(inputs)?; match mode { diff --git a/ethereum/light-client/src/proofs/mod.rs b/ethereum/light-client/src/proofs/mod.rs index a0f1bdbf..23b9025e 100644 --- a/ethereum/light-client/src/proofs/mod.rs +++ b/ethereum/light-client/src/proofs/mod.rs @@ -20,24 +20,135 @@ //! sub-module. use anyhow::{anyhow, Result}; -use sphinx_sdk::{SphinxPlonkBn254Proof, SphinxProof, SphinxStdin}; +use serde::{Deserialize, Serialize}; +use sphinx_sdk::{SphinxPlonkBn254Proof, SphinxProof, SphinxProofWithPublicValues, SphinxStdin}; pub mod committee_change; mod error; /// The proving mode for the prover. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum ProvingMode { STARK, SNARK, } +impl ProvingMode { + /// Returns a boolean indicating if the proving mode is STARK. + /// + /// # Returns + /// + /// A boolean indicating if the proving mode is STARK. + pub const fn is_stark(&self) -> bool { + matches!(self, ProvingMode::STARK) + } + + /// Returns a serialized representation of the enum. + /// + /// # Returns + /// + /// A u8 representing the enum. + pub const fn to_bytes(&self) -> u8 { + match self { + ProvingMode::STARK => 0, + ProvingMode::SNARK => 1, + } + } + + /// Returns a ProvingMode from a serialized representation. + /// + /// # Arguments + /// + /// * `bytes` - The serialized representation of the enum. + /// + /// # Returns + /// + /// The ProvingMode. + pub fn from_bytes(bytes: &[u8]) -> Result { + match bytes[0] { + 0 => Ok(ProvingMode::STARK), + 1 => Ok(ProvingMode::SNARK), + _ => Err(anyhow!("Invalid proving mode")), + } + } +} + /// The proof type generated by the prover. +#[derive(Clone, Debug, Serialize, Deserialize)] pub enum ProofType { STARK(SphinxProof), SNARK(SphinxPlonkBn254Proof), } +impl ProofType { + /// Returns a boolean indicating if the proof type is STARK. + /// + /// # Returns + /// + /// A boolean indicating if the proof type is STARK. + pub const fn is_stark(&self) -> bool { + matches!(self, ProofType::STARK(_)) + } + + /// Serialize the proof type to bytes. + /// + /// # Returns + /// + /// The serialized proof type. + pub fn to_bytes(&self) -> Result> { + let mut bytes = vec![]; + + match self { + ProofType::STARK(proof) => { + bytes.extend_from_slice(&[0]); + + bytes.extend_from_slice( + &serde_json::to_vec(&(proof.clone() as SphinxProofWithPublicValues<_>)) + .map_err(|err| anyhow!(err))?, + ); + + Ok(bytes) + } + ProofType::SNARK(proof) => { + bytes.extend_from_slice(&[1]); + bytes.extend_from_slice( + &serde_json::to_vec(&(proof.clone() as SphinxProofWithPublicValues<_>)) + .map_err(|err| anyhow!(err))?, + ); + + Ok(bytes) + } + } + } + + /// Deserialize the proof type from bytes. + /// + /// # Arguments + /// + /// * `bytes` - The serialized proof type. + /// + /// # Returns + /// + /// The proof type. + pub fn from_bytes(bytes: &[u8]) -> Result { + match bytes[0] { + 0 => { + let proof = serde_json::from_slice::(&bytes[1..]) + .map_err(|err| anyhow!(err))?; + + Ok(ProofType::STARK(proof as SphinxProof)) + } + 1 => { + let proof = serde_json::from_slice::(&bytes[1..]) + .map_err(|err| anyhow!(err))?; + + Ok(ProofType::SNARK(proof)) + } + _ => Err(anyhow!("Invalid proof type")), + } + } +} + impl From for String { fn from(mode: ProvingMode) -> String { match mode { diff --git a/ethereum/light-client/src/types/mod.rs b/ethereum/light-client/src/types/mod.rs index 556843d7..c63ca171 100644 --- a/ethereum/light-client/src/types/mod.rs +++ b/ethereum/light-client/src/types/mod.rs @@ -14,8 +14,10 @@ //! //! - `beacon`: This sub-module contains the data structures used by the Beacon Node. //! - `checkpoint`: This sub-module contains the data structures used by the Checkpoint service. +//! - `network`: This sub-module contains the data structures that serves as payload for the Proof Server. //! //! For more detailed information, users should refer to the specific documentation for each sub-module. pub mod beacon; pub mod checkpoint; +pub mod network; diff --git a/ethereum/light-client/src/types/network.rs b/ethereum/light-client/src/types/network.rs new file mode 100644 index 00000000..984b10e6 --- /dev/null +++ b/ethereum/light-client/src/types/network.rs @@ -0,0 +1,68 @@ +use crate::proofs::committee_change::CommitteeChangeIn; +use crate::proofs::{ProofType, ProvingMode}; +use anyhow::{anyhow, Error}; + +#[derive(Debug, Clone)] +pub enum Request { + /// Request to prove the validity of a sync committee change. + ProveCommitteeChange(Box<(ProvingMode, CommitteeChangeIn)>), + /// Request to verify the validity of a proof for a sync committee change. + VerifyCommitteeChange(ProofType), +} + +impl Request { + /// Returns a serialized representation of the enum. + /// + /// # Returns + /// + /// A Vec representing the enum. + pub fn to_bytes(&self) -> Result, Error> { + match self { + Request::ProveCommitteeChange(boxed) => { + let mut bytes = vec![0]; + + let (proving_mode, committee_change_in) = boxed.as_ref(); + + bytes.push(proving_mode.to_bytes()); + bytes.extend_from_slice( + &committee_change_in.to_ssz_bytes().map_err(|e| anyhow!(e))?, + ); + Ok(bytes) + } + Request::VerifyCommitteeChange(proof_type) => { + let mut bytes = vec![1]; + bytes.extend_from_slice(&proof_type.to_bytes().map_err(|e| anyhow!(e))?); + Ok(bytes) + } + } + } + + /// Returns a Request from a serialized representation. + /// + /// # Arguments + /// + /// * `bytes` - The serialized representation of the enum. + /// + /// # Returns + /// + /// The Request. + pub fn from_bytes(bytes: &[u8]) -> Result { + match bytes[0] { + 0 => { + let proving_mode = ProvingMode::from_bytes(&bytes[1..2])?; + + let committee_change_in = CommitteeChangeIn::from_ssz_bytes(&bytes[2..])?; + + Ok(Request::ProveCommitteeChange(Box::new(( + proving_mode, + committee_change_in, + )))) + } + 1 => { + let proof_type = ProofType::from_bytes(&bytes[1..])?; + Ok(Request::VerifyCommitteeChange(proof_type)) + } + _ => Err(anyhow!("Invalid request")), + } + } +} diff --git a/ethereum/light-client/src/utils.rs b/ethereum/light-client/src/utils.rs new file mode 100644 index 00000000..dad05b51 --- /dev/null +++ b/ethereum/light-client/src/utils.rs @@ -0,0 +1,29 @@ +use anyhow::Result; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; + +/// Auxiliary function to write bytes on a stream. Before actually writing the +/// bytes, it writes the number of bytes to be written as a big-endian `u32`. +/// +/// # Errors +/// This function errors if the number of bytes can't fit in a `u32` +pub async fn write_bytes(stream: &mut TcpStream, bytes: &[u8]) -> Result<()> { + stream.write_u32(u32::try_from(bytes.len())?).await?; + stream.write_all(bytes).await?; + stream.flush().await?; + Ok(()) +} + +/// Auxiliary function to read bytes on a stream. Before actually reading the +/// bytes, it reads the number of bytes to be read as a big-endian `u32`. +/// +/// # Important +/// The number of bytes read must fit in a `u32` thus the amount of data read in +/// a single call to this function is 4 GB. +pub async fn read_bytes(stream: &mut TcpStream) -> Result> { + let size = stream.read_u32().await?; + let mut bytes = vec![0; size as usize]; + let num_read = stream.read_exact(&mut bytes).await?; + assert_eq!(num_read, bytes.len()); + Ok(bytes) +} diff --git a/ethereum/programs/committee-change/Cargo.lock b/ethereum/programs/committee-change/Cargo.lock index 5eff36aa..906088b0 100644 --- a/ethereum/programs/committee-change/Cargo.lock +++ b/ethereum/programs/committee-change/Cargo.lock @@ -203,6 +203,7 @@ dependencies = [ "bls12_381", "getset", "hex", + "serde", "sha2 0.9.9", "thiserror", "tiny-keccak", diff --git a/ethereum/programs/committee-change/src/main.rs b/ethereum/programs/committee-change/src/main.rs index 6c854074..5dc3e851 100644 --- a/ethereum/programs/committee-change/src/main.rs +++ b/ethereum/programs/committee-change/src/main.rs @@ -80,7 +80,13 @@ pub fn main() { sphinx_zkvm::precompiles::unconstrained! { println!("cycle-tracker-end: hash_new_sync_committee"); } + let next_sync_committee_hash = keccak256_hash(&store.next_sync_committee().as_ref().expect("Store should have a next sync committee after processing update").to_ssz_bytes()) + .expect("LightClientStore::current_sync_committee: could not hash committee after processing update"); + sphinx_zkvm::precompiles::unconstrained! { + println!("cycle-tracker-end: hash_new_sync_committee"); + } // Commit the two hashes sphinx_zkvm::io::commit(&old_sync_committee_hash.hash()); sphinx_zkvm::io::commit(&updated_sync_committee_hash.hash()); + sphinx_zkvm::io::commit(&next_sync_committee_hash.hash()); } diff --git a/ethereum/programs/inclusion/Cargo.lock b/ethereum/programs/inclusion/Cargo.lock index e3740bf0..3af8f118 100644 --- a/ethereum/programs/inclusion/Cargo.lock +++ b/ethereum/programs/inclusion/Cargo.lock @@ -195,6 +195,7 @@ dependencies = [ "bls12_381", "getset", "hex", + "serde", "sha2 0.9.9", "thiserror", "tiny-keccak",