From ec1d98cc3ab65c447ddc2182a1bd2f1cc27574b7 Mon Sep 17 00:00:00 2001 From: tersec Date: Thu, 12 Oct 2023 03:40:15 +0000 Subject: [PATCH] implement deneb block and blob unblinding (#5496) --- .../validators/message_router_mev.nim | 79 +++++++++++++++++- docs/eth2-finalization.png | Bin 205614 -> 0 bytes 2 files changed, 78 insertions(+), 1 deletion(-) delete mode 100644 docs/eth2-finalization.png diff --git a/beacon_chain/validators/message_router_mev.nim b/beacon_chain/validators/message_router_mev.nim index ae2660757..2771143e6 100644 --- a/beacon_chain/validators/message_router_mev.nim +++ b/beacon_chain/validators/message_router_mev.nim @@ -9,11 +9,13 @@ import std/macros import metrics +import stew/assign2 import ../beacon_node from eth/async_utils import awaitWithTimeout from ../spec/datatypes/bellatrix import SignedBeaconBlock from ../spec/mev/rest_capella_mev_calls import submitBlindedBlock +from ../spec/mev/rest_deneb_mev_calls import submitBlindedBlock const BUILDER_BLOCK_SUBMISSION_DELAY_TOLERANCE = 5.seconds @@ -124,4 +126,79 @@ proc unblindAndRouteBlockMEV*( node: BeaconNode, payloadBuilderRestClient: RestClientRef, blindedBlockContents: deneb_mev.SignedBlindedBeaconBlockContents): Future[Result[Opt[BlockRef], string]] {.async.} = - debugRaiseAssert $denebImplementationMissing & ": makeBlindedBeaconBlockForHeadAndSlot" + template blindedBlock: untyped = blindedBlockContents.signed_blinded_block + + info "Proposing blinded Builder API block and blobs", + blindedBlock = shortLog(blindedBlock) + + # By time submitBlindedBlock is called, must already have done slashing + # protection check + let unblindedPayload = + try: + awaitWithTimeout( + payloadBuilderRestClient.submitBlindedBlock(blindedBlockContents), + BUILDER_BLOCK_SUBMISSION_DELAY_TOLERANCE): + return err("Submitting blinded block and blobs timed out") + # From here on, including error paths, disallow local EL production by + # returning Opt.some, regardless of whether on head or newBlock. + except RestDecodingError as exc: + return err("REST decoding error submitting blinded block and blobs: " & exc.msg) + except CatchableError as exc: + return err("exception in submitBlindedBlock: " & exc.msg) + + const httpOk = 200 + if unblindedPayload.status == httpOk: + if hash_tree_root( + blindedBlock.message.body.execution_payload_header) != + hash_tree_root(unblindedPayload.data.data.execution_payload): + debug "unblindAndRouteBlockMEV: unblinded payload doesn't match blinded payload", + blindedPayload = + blindedBlock.message.body.execution_payload_header + else: + # Signature provided is consistent with unblinded execution payload, + # so construct full beacon block + # https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/validator.md#block-proposal + var signedBlock = deneb.SignedBeaconBlock( + signature: blindedBlock.signature) + copyFields( + signedBlock.message, blindedBlock.message, + getFieldNames(typeof(signedBlock.message))) + copyFields( + signedBlock.message.body, blindedBlock.message.body, + getFieldNames(typeof(signedBlock.message.body))) + assign( + signedBlock.message.body.execution_payload, + unblindedPayload.data.data.execution_payload) + + signedBlock.root = hash_tree_root(signedBlock.message) + + doAssert signedBlock.root == hash_tree_root(blindedBlock.message) + + debug "unblindAndRouteBlockMEV: proposing unblinded block and blobs", + blck = shortLog(signedBlock) + + let newBlockRef = + (await node.router.routeSignedBeaconBlock( + signedBlock, Opt.none(SignedBlobSidecars))).valueOr: + # submitBlindedBlock has run, so don't allow fallback to run + return err("routeSignedBeaconBlock error") # Errors logged in router + + if newBlockRef.isSome: + beacon_block_builder_proposed.inc() + notice "Block proposed (MEV)", + blockRoot = shortLog(signedBlock.root), blck = shortLog(signedBlock), + signature = shortLog(signedBlock.signature) + + discard $denebImplementationMissing & ": route unblinded blobs" + + return ok newBlockRef + else: + debug "unblindAndRouteBlockMEV: submitBlindedBlock failed", + blindedBlock, payloadStatus = unblindedPayload.status + + # https://github.com/ethereum/builder-specs/blob/v0.3.0/specs/bellatrix/validator.md#proposer-slashing + # This means if a validator publishes a signature for a + # `BlindedBeaconBlock` (via a dissemination of a + # `SignedBlindedBeaconBlock`) then the validator **MUST** not use the + # local build process as a fallback, even in the event of some failure + # with the external builder network. diff --git a/docs/eth2-finalization.png b/docs/eth2-finalization.png deleted file mode 100644 index 1531b234966ae3ca46a2fdd7da5c6ec1e2c746fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 205614 zcmeFYc{tSn`!-Cnl`SdRL-tViUD=nAlqGw}IwZ!Dwem>_*^7{U-!)^ONo0%3Iv9+7 zVyt5sj4{uzbJR#=7(u*e{TgklmFlUbRVgr-C#WBY@%zTZ#u8Q5X4(+ z&_h5oD9|18KNEjO{p`v!(VcLIxCFf9zmMzA9i(t8#qyBLsabGbsStsj_3$Do%n!h~ zX&D1D&dHNyP*AUobg0zjro2&o8|*xZ&AWm~U_oXGh_6IE;3GL^jOK?`mL7Dh`PP(T z$|Mjb(tB&6VQ;;s%h=|z&0(9!g^tA)wg!Ur!{k*Gm4b!3$uf9_EO`9j)_fS;C(`hx z^~>(4J5lzC>JaB>0>YYNlH-W`8N)OFg;uN;9RCtSQIS}yav_Jz=H4-8u?DWGG>M;v z55sY>L`ez;YV*y{bbrr%m6pOhldbsMx~c$bh>WMNNZz4nI&}TcnA^2lsdhR`_Y6$Q z$7BBvI?FPjRB|y?#zu{s;+3F#)GKe>nVPC~th~t>el^`(N6|NBHzV_eBbNm=m>ybB zP@O%j%^f>)ja+(}W_|PZzez3(cGhOFoMH&F{)$UZ22pL+zIfgj(C(tmXvwy6@H2KG zGHuNTZjia@ieDW)mU1ZSFr}`l%4_SFc!G4vqK!PATLlyJ&s|80;v=|53_yrSr^ zLdn8|XA?ltO*IYJlK`u55Jcfj6{ z)RH?A!DyOZU@Z|>zZjWh{gV2|$fiy}R|;ce?KUB)eL|h;>Wz_Voey&583DA8{#XCj zc>gU27a6Y52tzMFG2Ra=A#kzhsB5(&v;W7yt0~N{l4Ar8hn<^K{O2{y2#xo1EWSLv z>H;!onrPp%k!=}rEohXNI{hV2ozyJuFM$X52n>%i(BPDHc(CH%!fcCq$^XQ>4G^kB@6GJSJ$K0MF=TQYg> zw3b#eJLdBxw2W>wH`>6h*N#{nTi%fX=zT@42ct#DD}v$}`UypD906%?r1s zI{kR7@T9>VFMje5yc&+AG1Hm76I4nvnYWxLd&ASxZorDHGqC7sXImJY9i`d)#yrnA z-xJj`J}?@Y_QcjE1A;|{(puILIp=OP=7O#veXrF%um`GIj9gv_7opP^kaZ4v0}`_P z-sQxY4~@+5+I!O_r_NS?bJR;(6-@4Ngx;VE>>9}L#USXx z;+;=y4k@ww$LNm;w*TdHipj-}sngn5bY&0I*6&xm2s%-j$0a{s%@B(uejvPO)#yP` zH4KO`qjI^5rXsz_?#(ic2B~Ldcr|FIN}%=kab0pkX*pF6CY@P{;34E-)WW_+b<%cu zgZmOxpdwv4@|*BhCFl5o-W2@+6>sToVmn^M0y9OxwpZxwM>fvf_NDnOUv_e>7O||l+~1e02Rwu3x{eUL9ASTn!n~zP!giv@odKxp z9}=UJ;l?;jwaDL`LAB=4Rdt&SF`&e2`6>v5s_>gHcDvmw}lnUI&>d41bu`Y`aMt&e0>XOa1s>ekSuYt@L>IT5G+L) zc~>(PBOapyvvESz2tvZJOe-H56AB_EjBn~guTGg(1u!k&z~!27+U622%Ij{o92j=X|G?8}Xgg&VJ9yqR3c z#dx07Le?GLWSzONkc)vNK<6vzJT5t==I1|ecX%7B{;_w@OctUlRktY=EsO|dA{|=k zyijTk%lDUO+M*4XybPs6-R4x=0S3Vqrq7brPpkrJ7P5g3sW^6$Dj^>TxAf0r57 z-TwX-KGd`&Xg{;fi5bP2qo*E=-F=(952Kzi-oTUiI58X+bO3(R0=L(4`}vOwi05Kx+pFT$R@*dtOh8H^~W(g>7k?HX_F5>5VN0wru@drPFCw9z{s{;Vx+_c&-*j1muhuHPfQ*<#eiT^TEVNYTi5k}JCucl<;B0;oBwYlvWXM2VJ z@NtLd6y|4{dW280VK$#8y$7gPK7Nl(%M}>mwog`%fZ}K6-HgmGe_S)qZQ_vkJf^>fdglvUcn3_0PzZg&=W= zud`1FlI?TMlkjyhZDu~GgvA#&IeLVZSSRwyRIg}_wl=j4NM!*|{|dmGoKg8nn(OS{ zV9st|bj6L?IMKs8$~9AS^RdV@NcDj5IK2%fi0;}OJ3aLm>U$);g^HPhYIf~bglb*arS<9G2yKLcDJR74p$Lo)P3 zr4bK>8^ov9uAm?LwJQAdyUlw?_it&R!GO{}w$pEZO2c0knz~aPQ1ogO`$3XiOj&Sb zme$J>$fh6ut_J2&TU+asRw;abD9bBfaFe%H^5Iw6#7)nbO<0}dB`fB>N#DdQQFh)hfAJ0Ny!KM-0X98 zs>eV)#`<#PQ>w&XQ?<8?LP_NTl|m$IY1v0*|Hg?qA;io=Ii@qQ=iV;|J^((b!Suwe$xtik*WPm7&2RL-50ik3)_X z$xiEh4n>1lh1eyvn@OuB2Mie`V=ovVRpq;UHHa1L`<2>yamVv%5AA9^-&-X9?x^X9 zewk{4kA5e2l-3L>V>M*5EZZNMhGZ_b7ZL5rhUbWHVw$?t=FFW?P(cV$-_Rv1Ah#sK zIGmFhW_H34o4pHW(hHR=_p>Eay9DxkO5{WA)(PhhaCkx4ry>zbOpM0oM?nnbzpj4IFxeIH5K zmKWeM*$LOQ;RY5+UzQ$Xkya*RVv;(_{dFv4TggEHEu#JoqBPW6o@hH><9oi+i_FF{ z8G-R!rGVs)&V?HT>?o!_G7bE=H-ZU}LdP)j{xtS9PXLy&Njcn=f!@>Xv$P|~`F2@I zn8_2=cB5s7`{DT$%yNLk>=X>fdqTi_F8e)5JwwXkiP3IZlH(G)Has!7veATtO3eV# zJc?-H|4Kx+2NdRH+!Uv+g`tlIV8Rht|BmyOoT++&q55;LCod?7*VPCP0Nlas+c*Z{ z`jJ`y*TkRUovbX{+LlnDu=NnL5P_VehA&-4anwGjbPwC8506)JaB-Qp5C}f?kTc=} zVnY9yJ#bjS`@g+3WG3X0pHX?u;?!WDCXu?HURmtKjbG^l3RPE`b<{SpGgybti1=Z2 zKuwa#sj?SaZMgP{QE!4bZljnovF**qmv-I3&47LoCiTw085>avxa#-|cZ%Ch1_>%U zP-~6k_AOPGa>1 zX&d%QYG?7aA9k{w@8bnSKX*Z0TvP8lyPY-}_R2_<3uQ)Ky za}*{@f4RwfzGF=M_l;|lOWMK<1j`?wy3}3_Jm?H2oZDgUE;1C#@ zrEU^1AbfpkkPX&IML&1gj@|bWwB}J8*Z6N>wa1*b%+_2%f(r{r@d7J}8F*RYKk*1B zZPh2YW$A%i2FW6XR4OIb=ap{N4Fa{7o2eH_Jq0BU6zZ@ksAqUHD~sy-asff{=ocRA z$A{hdwIMb=2Z%9n&2P*vJ!hVy^Xr<#^w!YS0eC9>(8%w<{p}!L)L}&ezRgTnAI}o{ z(uQcm2p-xBk)0&QfO1Fm^-GYH`-s7jU!-m1^+ZrFRsL!APDr&YdEQ_`$s?kCt(|rU z`g-Wxi-CfRBVLBr$^<&T_nb4i-uU|BOK}=}pV{*@iITHk=f5~oJVwu=ep4cgebqDu z^pxf$Q*as3*C&M09Ly*=ddrw9gCOCPD;hmk>z1+SJXQ_i{Y$^WQgt8CH(wP6^;lW~ zn`T09;M5awj_v9IHL&5TwoWe$yWgkwq)h(p-dE<7NmE6j*T*i+DJQL>;r+6Qml=(C zB&jM{Dne{^Okp~gYU?Qg7#$1n5m8L%gXD&d&V+Y5hi_5+HCn95LM7{R+2$GHs@601 z{hF5vuDdai1pF})0hjR6g3zsX(b`5>)$l|67+ ziB^kkFV&B(f%-0V&G(k;|FZfjCJA2RXXTY(An?f9iz6lH%+JgVLUjcogVdFk^j`OC zD<340+CLEF?0pnVt`h+TZS{J?I`a<|VB*%Xuub8=>^}D>2_MLeim&)~r4yB{?Vebz zaFqrrdW8jiUt;q{z{7;a-_3DgL>2agCo5&^-ff2>%UP^c?8!J7-f#Od&Ja>IYP>uZ zP6goV{~EQTLR}(TC(BKjuY91#ADatY_tT>@yaIE(cjBqJQP_I>No!lReyBDKbD7O&OqtIVq6U90b_~TT^8si zCBg$ae$_A&HHQ`J7=ItJKA3AwCLz+F8vRk=*yIWPLSgii8usr*>6lG5)*{8*NyT1; zF9&JJ$+S_jo6kUdFG2;94il!NG88MF?T-=tf5TmGBIW1>Dy@vBgjPE9{5R1pbe2|W z{_rKbXQHpcZPWjI#J1FxUV}49QZELAA10<+xM7(r}sNFj8QoB47}xp|5zAWw}*qEun-hGq4E~tlD1; zSNg}l=jW(?ZiV`l!rZQeep8zsQ!ad~!AQ7@uS&>!@9n3z90ZECz--#Ybt#9HU&)Ob zg1DM*hkoIWxwk&%w%$#ntPFj7Yq96n06(?xD8QtWPll8!I8FBaNL@B(%IV7_+$&t0 zRMKI<{4V8Mu1ssSTUN$|I{n`pU3feX(^F5);ceSJl-AfW@Q8Xf_uS?Q55978i)H=_ zJcXA0c61WARS|TV!aV0;@*y%RdiNOqq3Y!~eOS>+=PM`u`0vlD*X|?(RMCo~Niiv0 z(a|}kiUtF^tRd9-C1yY4{U#H^OaC~Le2cnL>)KX`yau)N)PXU$DPjG^r^oe!>Q4vi zhBg$NBzej5Z~j~erX+m$??DtteQtSpl0y3xjJiA>CzJtX4EpNDl6?C6r*Sh z+*P7?tVlQ4$~vl@kA@xpHYt6xUoz9d^Tc1tVW&k!Vf*Q!@P5AeSFxbPWxR{JU{y$w%48li z6TiA13Jeatf z;YeZrc@E!KE|3QO`;_iEsPvs@5g(#|hL0@6Hc!6F1}6 z=?kbvGn2nYtiIX$Z928Cqd?lx%MXvAehXE<(00A9TG)Y7<8$981-GtGux7V!Xt@se zC`)a<=uQ=T&%?&hwEZvWkGIN=X4?|T14OKH*MG2gAN-nn%~W`+w^`}RWa*7<{ms&u zkKQ#)fK%mEuIm(=fu-T6xl$z!fMg1`UV0YrcjzqHx`Ws1h%ydEzeOB`jcZj#{oa!bJmPIc_}xz5Gh&>Q5C1%K46{ zp;yH|ez_DI50?}s&Yf8%A!R5l$!?x!Ad{$3sGfUHS!=JIL)Ll@X|W6z-;wG(u*<#$ zGBa&)X~6F3_^Lj^Q+Alu!GfCEgH(BMmHxO9_%I;&x3l9(o?E4m!=?f6O(;~}{!L-_ zTl}qlTRcVBy%8sZJKVtb??IdXIcPD5BFvrglDSXh-kCur=I%K4j}imk7Ix}OBj zyJ;|)KEgP9+0I{|XVJi{Z|)`!)hc=1;_v`n4s6$%jp?VyX|`o zP_$pFUgF7N{Ea@)OVe$+m(JD4Djki-A6vqv9S9=s>{aA#?{94`D@JY4%3ta*JfjJ^ z&HLH#FG~IAe*WM2RS^Cz?teZ8qQw7$g~8r4jQO@U|MP*1`@j32|NmF}|4-omn~n%Sn7^G-bPyFG zD7@s(T$^m4PFBP74EFkb<;@!^TBjC+IB|ZJpEegT+epWJS zCMEWa#@D23Kp(5+JU?xIvof6{DO0X+K^5TD&``dNr_<~9Sy#`=539>#1KVJ7sQ4@T z*2pzlQ*M|?i5tHe#_xphqz6B)l_@Li&1lb5G$njOpucC~@W>kThsn!m_`>@i%&&Gm z7h8*dvCZZHGomn`APc|$;7ySL_Ke(1>JFPM7oFwWcNuEBnR~DePr;F~GI`kBk(qOx z)S=lwxetH|bG&gwu+MTE{T5m^ts>SwlqAX=l-0jb@my4s0i+{B%bj7 z7e%8Pew%&8&-UWV`bZNhI>mhr<3mH&4;W;uGXQpfw&+F#n{z z_2&23;wr5F{ZvFA;N(4A4>wl7QWOH>8??xmk!RMiIbnW=5FYe4&CI1Y-7@vuQ~48| zjxdAev5oe0>p5jpP6gc0Hv|wc|B~9l+MGm?`JCG_{H0*2cl44NG(Q*>ea-izA@UJ> zy6NabHr%~oV6?|3{dl>1AAxP8JU6~pT3s%GvA3fe>cp3;t9yKknAhHVM=q8E(x$a! z(^Uq+`yD2zjfDtY>o%Q%6@+2+49`o`-HDfqT{enrjXywq)k5fqOT@ho1NpnL6xO*) zT57Gj$DC-5<`ya1&2N|e2oQ+Q%E}mPtMNReOIhguPCXT&xb66n2F4 z=iUH*&h0M>Qy>2-BzF0YQUX-D(2-5Rs6Q3FdGtsFb7Cm0az zGl%$1ZY@X(p64F&=dMwE%voJ{zz>(;KF(EKfQT(!8|^nO_es>*wEz`V{0ZXhj2 zUh-4j)q)%)KdH8*mE+hl_t<;gdOy)Gb56;;b#YBZf>Wv{sOulw=pPjqQ^wRdSyxgr zV||nRm5qIhBj`xh$=)Mad*qoO=_o6K9rZ8^D7B}lNO$Hh2Z}`FRK%XOFtX5U7$yDt zDKj5`J73ww6)O#&&e-`3u^VnDbB8;{+rAQzqEciE zOyCfaX?+u5z!}9yo3;#WjO^0~0*8A{M%?>yElBU%=Jwq~f_VzrlDnS>ap|5BYsvAp zz&`dX@=dQR@019Q{Iw3Zs;t7pg=yhjsNl%G2f;leIo|^jTABJ)5f^Dji2)$ld4|q9 zv<}{k20H%xjKi`8jk0n_BXo~voR0F1QuRTIKjk1xcexg4Q_r}!g?2zGk&Bu17rEn} z+H7nHCpuC6aGF^h+T$5K+suK>{~`(7GuRl>;DT8l!xyD^VsSI>hd!P^yhFHa>5laJ z4oz;*{SzVK=(fZWseWl_*H{46EF5`kjzYia{k8PqRmGcwu(* z>plppwjpO^TDd&Qfj1RWLgk=$BJ`_ z?61W$CeMP|N{^ByEHQNorke1KA!G0?Ok`ospL0hecALuxMNSDQ)WR00hW;z2+`8t4 zvot}}&u;FxcDgQHo!^ckTOpP)$eh?T!axO~_R3j30#WXFBm~CLLNbaUqT&m}<_z32 zuD^pnqjS8F+#O|nkV}E^_&^eckJ92>VNBB{6*N9{5m8USE-cM#Q@kYc+MqmXG0U9u zw;bYtDcVz#nx=w9u+XSF(xkc$f!129m@blAN4w?|+pBesaS0`DRgW1eGbRIU=gBnM z&j?;qlKh`~zP$`#yq}Zm+sG(_=M72VwtoKA0i7uTL8y zT3L7W=lMESN!hYEeXBc1YIw!@oAWO;u4B=W_;Xl9afsH#gi%qP*HTkfBgeO~*VOOZ zb3Dg{QA4IDVl-bnwWr(c{e83hyAG<|q8d{Q4nT8(a=f8P)ubJc1v!oRUiT=%6VYdd zYnc5)w^@l{G|W5+0gAeg&k0)pW|Gdy3SW)-+>t_)J1YS1Zg7c^UkkY5^WkJ}B3N|f zJ28M0ZSh+_y014XcJ`;!L#i&()(zWBBDf zKY0P3+IqQX9ukT9k!eORb02MlP$^{Z%-{C$>(qR0*P{E7JM7oZc%IMQ1(ajJP+K%l zy19Hth?!fo$p2Dl9Y@|$d$|`Zzjdg*4H#R{`EfH;*Uc(Wn7<*;beZRfN6hf1*rzj} zy9`M@lLO{5?KI@fEA*YZmD4t)IT`Ms}*yE4B*^*07{O)Crmcdn*y1>Zk&BC*A& z3i^@-G}}WVjzY`E(~r$x@BFSXAie56j7f-zeV9*C$K`73(3~BIj`hd3nlzz$iYehC zP^CQXZl8&(;?#^Uo;BoLS^-~J*uxLRhH+C?KH}eYWmdY|k9$nVskL`0TIUPesL%Lg zsz*eQMiZnR7Ir-YVGw1U$JQD69V~X(TKe=v4vS_pomN7->{VAxlGHCitj@Q!>l|-8 zY_&hRFQj`MFst@(N?vr}L9FtE$hHQ4DT}S6`5w=-> zGCE+z!RM%Ay>tT%O2>9VI&yH#l0Yy7JOiGNms|g;)a?HVap&r4e>!^_WRRfEiYzmz znH#)?cd3xrQjUwnUjk-bphLq85FebPIEQWFI{sk$If1CtDDjAOg8#fjHv$}+9$|X+ z#G2!vy=sreb1z&uVY}itA(H{Wp|w<0oIaH6yk7+#r?rgX5pJt+oLbE~${Wq?WmuUG zx#iEj079Q8&dLkm-T~$b~%m>wm62S;d%+R-sOd)YnCg1F74{z<9w z`z}e;05FDHfYcFwMi|1NH-$bmcnYiKjNKXoQG1-S`YAuH;cs&ZAot^WM*QcDW7d`( zFk)*l3rSHeXT>(tfm#uXJU5Eh2QRyhc1S;l-RNI3f|r|~051pvRwEj-)g^j8*%lm^ z51L^-mx(06%D4MIPBB8fn}kAJs0XkyQz1^yXafU^BxKml+sXn zWSYeaPUOY)>24zVG)O+{d}UkoS?#uT6qG*VPS8c!2)bkJY$MnGYw_1RI#B*iH3xm+ zP^`GcCGJ&w)7mYiSTxS=bvUu@-HwijPZkF)nj?Yt-FlLb7>`WX?a*8OSmme?(`_{L z$Cf0C?Gt&2RWe3n{vsu6M#Ig-`=%ux!kds+LEO^&^&xf?eYYt(*96ku7PEo>S%Sg6 zxZk){Bb%^tW_kh>8fmm8y+B)ST_?1EP8g@zBvjC#p&!fuxx5_UY)a0P)@IZrMk@M!%>3 z(GEUTIZge#g+w@P`Mlwv$xW(w9ziRV7Ju}+r()#$~FmB}R zWiz@6)nVTlmc4En9p2vhPxGh?Bn)jud&E2CWMyzP=zDc)ZSW}#+Ju`Mgx>dS0i~|K z`n7#Ae}DJwEp*IT4iDFAzVK|=#RQP@e80_c2%g~gfe=PiiPhD0FX(`RbDn_ae0mPT z9{n!Q;-SyL-4`UhUN51?TLVs$w>QzuvTg;Pye{6uG$*yD`phbA=Wt=rIb!4L;4F6_ zOQ`E1tjFy1{<%)gj3Zw7lTLqDUm@&tRGH|aypDw97RURVp(;tg!1`>?<7mVqt~|T| z;pZ~n{8WR>=F~ZbC@*-IK1*OIl&!Qba)ZMpf{HuMut9ENaOtRJ+tdm6jUAYVpfJ4E zq_Vu%73)T)9|X~^ANF(ZSwG9%Ndlj!A+1a@xK zf4istsTt$EWTWOw#D<+AsJ1c;p||~bsC))&K=#p)goc|AD{0M32JP+0ZereMK!ZNq zz-Re$11)2hQx?;!hl@+0ckYJYuK@}(tt2q#4O{%@groRb4jP~8RplGoJu{x47@tOZ z_4xZVC)|hOz_&=*2b-Fl-(n~Us&P9L!7AcY=22^x7X4dqBhStMvT51A{7QZClw0>h z=gqSXwdZe_+(KaZ>l~$iw|5?NW_78yLreES1Uf&|_a-7CG=6DH-GH>Bl?kAqQ^7$* zscDB)L;~)GCEp;s^Xqoj%f*kItW%$o*DCKIglEBYtcCLZhj|He*6Y$ymv(W2Rc%I% zR_f#o@gu25Rc0k;UuNyPM$ZnF+H0ui^sW(I?wjxvW;dWevLFARem?15+vCT63HyVB z*t?-1ywL_@r_b9tu%+GoLkL0>g)FNU(g{8-b_he^vlZY69`h^C_7KZE3aq=^2_*fE ziXqH?w`pdLz{shRK}u$)TY>Ok{12k>7xAfKff`;;Yn9}g{-`@iNAjJ#lmw5~SvGK+Nxx9&U}Jj#s95xdC0&L`xaExK|ozMSoG}^ZPPK{;2`}IP+!N&$-pZa zKn>FI{&I7?+me%!LR_#5FoN(Ab_CQ#`)&I=n(fCNr}w@K&-z@PumDGeOFkx<0Lx5W zIl70RHtgOP58jFADgYbOGQNGVXndSsmO1^qCqfY4ZctV>9ybF^nM_|L^-cI2u1@8( zyIwPb8=S7rN%m4b>zd%@C``>|;8S0jVPm0AxYP8VRoC6#5_m~0*(R4OFu=Uxtn6q1 z=)oM&I6=@XAfBf*jGrtd^{Df_dl=v1O$q^vcRDlb67B_NZHj9PTfMLRDV?cU%+M-k z!_}$gm5k4IYgY+A)W`R%)S01mj#D95rSRf7pQP%FxLzv89Lx8D;T2&b-TVxPUYRJ8 z1v}4Od4g@Wm(Vb66q-(?Cs{Q*<7#0HOghyFKk4V3dNxjpMO8=mBuubU8jpPuByN8* z)C-*G;g35_!L424zH_J?P##}t1~L7@2%h<8!?*o9^bd}q4lZkuxl0;(cx1O6)NBT< za}A%c&1>|-2*|qIQnVG+Q+ASP;VEF z_uf84#VF4l+?+5dd^!(;4Y9_QZ4x#WI%*7lE3k*L6z1MS{y(mYyZBXh_(T%TfgKkm zXh&s(m-hSZ_ieHM7!V@P54Ef-*hgLYkUonGb{p&vFV$maWn*4@<~n?m&#;iAuC*4+ z&7B60n#n(6tea1P&E(T8NN{f4NO8w&U>zA0c!F_bo4VvbeHhw_*|8b%Vd{}6vWJAQ zR+3&co|%vEZ|;Yx7^u7b!~{5F*UBCyQq{Vqp0&)h#P=Fc)%wIsadH=t3};^jwxM4? z$Cj)4F!G0R!))KLzH$o-FD&BojeI(6Jq|*`j+B4i8r|C9Trp!wrC$ho!EZ9lpwgt% zq5c-9>H)8huMaC-qIJd^g{S_bFXnR@ghc$~y!{d@gq!-feuC6izCv9ko_7-RkP97) zJLMk-?QuI}M}3d-KA@-Jex8S&veah+v`RaWR zG0Lh-fG!KpgvO4{>3k4ta{su;c-4-%N*VS&oHd2Z15#4S@1N^7tKP@(Evw$pJNNjw z3Pm?InYp28K<}f>Q1E+cRpp)m(1voe6ne3}?7?>r^K6jhV!Ip-LSuRn78=U?c~c-K zU!qt@>GWQXaF`lUFxa1~zN>#|bL9ifAQ7_7XxsQ357uqAk6t#Di@lCROA(ho3^2!S z(^?3O*jy{>OSE}-gF9PHT}sn-7oWZoHB449leK}74!?YCJB-FQ3Xn<0LKNFq<$^(5 zfnlAh_0VdsuC^T6K8T;sP*q|Hf746cFzNccosq17`GC%S*Keg}q{W1Ws_03JP<4Iq zz3yyB@Vb(7$^Fx>kuF({hrAsoEVdayvMtuf5DHCOZOLuxV=#&7GO_%F?W&KiA45jB zopZ>!A3J4<7wk(%ExiN#&Vasf+^^y88jUM_CAcVJHD_DfF}U}$8|<-IFPs|+w+326 zkU@0=+g6BMHe#*3VdcX|af{(zpItr0va-GF=X3BQs?sL}QRk8~Mh=5Nqyim5UEa6EVL0oh_8c2% z6tG5AU_ry0+k%TH7BbI`=DwPqgaSo@%o|(+X!@KUfPB}i-A&}qj0^{Z4C8IRN)uK-FE!stAiBtImmcb?tA)b zvCJohCFN(}VPnwMs;s;hQ3$2QORC0I&4c~T&%5}K|IM}0`q$BX8EyU!7175LB7Vx- zC^bfqeTm@WUM|lj-85{wE!nG?q#bnlz;!vjtsEiZtSdqA;~R7>kZ~!H{|+-+L@yu` z&%*iF+}%-Qg&m9`IW0}-1T3Nax7TA-bhI#s(P=^h3L*YEmgE`bA|~ZdE(XRNCnz(A zr(3fE5Z>O+0~=KPRUde_1D!a*3Re2gD8!v1Y;C3c3z-j&e#Ggt(c;#6I>rK`^0-6u zJr;cwn*nt96T80of;~D9V0ZTC)}MsRsmmWzL%?r@-)y+C0h<@6iqpsr&DASNm{RWS z>w^7bAm^wWxkTx3;Tdc1V)YUN-Og~V%5;ai|FE|lpxfFUr|2!&W)$fHs-o*e95X*b znRK)i5}ws0)>yQ)m0eE^(#lo3%Zz_FVlS;VwX{IWwe-uP)AVaE_TOg^80vz9=&{~ydeajV^FmO;nJWKd(kT0im>($B!ZdOVTX28| zz2Ck)9Pm29;da7d-o4Q3IlJF-L;0=Tx{&jBbB*|v{UahaKl?>MV|CSb(e4+?lXj~c z9g;VxC*mf6L>f)UL)P!Aj2NFmf1Ily-MUJY^CYMMq9QZsMJi$!z~_$bO8V3IMZf1) zcz-P({NP$E(i_LH)EBIU3MLhMtgAd77&Y_@_O?AG%>QIxsjjFxQQHV!Ukq2S{wr+L z1Z5p4>wQ@1_O22FqU2XX49|>)+?fLPBtW&ZbHf40(1^vzG)+-lO!OBn|6r@T9U^RM^-U{JgK$5`X8Sp#z5AT99Ht(>`mCEYRXSC0-vR-@zjn45XS!?u(x2_7dA%8 z_IE$mvPTYyd2xSOeztKCh57U)YPPUvZ$kqF2&&k-lU3ZL)J!Qvn&5FGcr=_cnYJui7B zlk#w`c)Bu|saUJr0O0DtwG2~uj6drN&+3IO*Xs{%E;_u6SpJ<%z@>m-zh*XrmM#$% zBM;Gn5T892z|YDIvQITJWwK|*XM<_LDjnw917(F2dh4OayzfNE4K4OnTerr75plRa zQgyr3WBgch{n+@^FrOtAurG|O=PwC=45F&sF#L6W9pG2J7~PA)7c3Q(9+L>X{a^=g zd$95gp}x;PKYM&gpHi(^)uWV}_;T@D?k4>HPvmT4b)qb@y-F4#!!3B-2t?qWp5IxG z);F4iyyNLrQG`~zfBtS!uwQm3ei9P32fjHKoF6jqeEyLvj?ElJE>@uOH@Q!Fr>I|m zbuFhvri-zWX-9*7?Qecm4F2bSGFUdpFjbz*1HHqjkW}*+SlkI`Kebzde&HOeOxB_5 zz%7;(7N?+|iRyzeTj(ho`tS0UGF5U zOn^;E#_w?KGS|*`ZWr6Bq4f%zl?)pE21Ba9YDs4o73)2kMR@$G!m66*gvMD}F+$hVYI ztNL<`WIYL{KPh-Tr+*@^orTGZ!;W>)nyMYU#{#!^S)8x!&JKC!SmADJZ~Kbe{23)` z;`ozB&~8bFcl4`+-F*`mGfX!+gCX`T9BLDceMgrvqWynNzV2bPYdH7vya8bBnm zG(@zU?QEXyo=?GwuACct=`L0{Ec;G;Tgc@%rR_5P7&(Ula-2v0W@MUaPP9HPz_I); zQ*Vu_MoY&1xzEdP_S)YLvFJ&4fL5dT?L7V`@yVw^cI^RPb)~2|Bwp%F4!Fajxoz^g!JqE?WgXD=%%CK; z;P19wjuGJ*(=*!_3)+k;ng<_8C6ql3*@<+UMSCpc-wG3re$a1tww{UqNP}&aN@=Tc ztn9VCI;haXx|XWTF1O}`!WFcRTfBJ|3218o+9*UwGR3%nytu>LXZKF)ZUA^hy$Llt zMj&P%_xj<5VGSH!fgX*vYGfVQr6DMX?bz#_plQe)%fN2ns${Ac>W^xYy+qx(g0%&h zlxPKWM;xd2p-UA1g%Kc~)t6TqJ}@mOv6=*$$${-S3t1xUr*EGSm@|3&D&zh@^7{XH zjbEne0DsX2L1vv+*zO|%8g9G*+>39gESV&QxlcO~P07SWnVAk(GmVnTTEkf$Snx4J zpFDE%Dfv_q*<7YI`i6r*xL8Twvo>pOLl1-8rf($4hKLZieQ0@b#{h7%m4Zd`j333I zui16ZB)wX^6R+gc-?b-#c}*uLI5s@k$1rN+tlDWa(%xqG&aA}^H!!*YCV)aT3^axLyuL`?gd;q2Vhedzq3c#{>5vtsX^el-? z6MrBaP*HvR0>710x6ewtRiYOvC!zuz?KZnPmNioCakM{Ja9}ak>*F#%F|{9f&J#IV ztQU$P1P_7$Dz}$1(_)W%{96|II9zMt)9^=&tTqxr@xAVy zfQ33d0Mzt^xBnx?7aeRxVYGRpZGoZkrfN8^KR5m%vb{hh*3QlY5Y*&EbR)-Duh?0R zJyJOZoPGlnk*ps2o?VH3ab1JgF(_E<7(CHHr{8uEz`zZv1r{{Lx+8w3PgP z?d%7>CF7oAxEJhGp6}JukK%_Jk-{i^!Txq>QusAM90_jzM`$%&USuebcA85*yVWi2 z&r^#_b+aF!$z74!t;@-=M*cwsfOuD4u$)TWux_@O)EoVu!tkm;_`GhkIXUf&4((vF zl+O^%nXVB%N}bwV+NLlO*K5#i>==5hzl(3+e|jp z_?xQpDnDO|yX?L`rtSvIo|f;gF^2na`QF#eUbDH%q>~tjs!sEBpH|zd4=AX3@pErM zc(avubbbGf2#E!uR2Xe-N?HyRa3Yx!m4gIho7F_%NP7S-OV$94-l4j|g*j>6NL)vZ z%&xE~W_1F@1aN(&&rZ>$8-{DtI$%>YO2Nc-=6@Vu^DJ^P&(U1zkTrM&tC1LEIgUH&wt0X z1N?w83X4Rc8TFy2p6|IjNIz#zfVunU+20x*aEIVCA{TN#S)>10ynZv}VvqRct4gmX zRbhd>4^J<=Ugul(c2GLgIT_rd@n=$1uixc*vmSAwdu1tsZmGrPPPCm5AGhzn4rlxU z3s%-_;lHNw%wHv~Q}^OXmzj^PMljcG=EmFCH9ilH81SufQ)_f;_mh$7Q4F<5oH-BQ zZv_sYn4~R3uS^~KQ3aVx7_lBmo^?FFQfa*k%MT5=PL|-N#me+k+@E7FvNZ^1(&Ms) zFz&K2N=JQ8=PoIAM(2x2@VqGXIY5Bz(#Hg^$nl$ZQvdAzepSC>S#qNyC%Rhv; z%C4&MD15nY`(OO9yHrHtC;EYT$6)_F0+=0@!ug=rpjUi2CIJi zadxTRh*t+#U98WIFdoJ027&%xPX_W2a>Ydus*!Q|jk}Fzwqtn6kQoY(@(s051N2*A z*~C-$1?PyUA*MOL*l5k!Hyble(r^w@T-(JdM>SU(pZ7d;D(}xeK`SoJT??Xrp@z8SjhpZX!93rQ&!pEP-VFwFYXk)jW# zAwoaZ@fA!%vs3##TL(H;fVQCQm`)B`KVr2n$7C;L@TG?~pnY3_KYdxCf7ixLm6t?_ZV)h&PcW-T3JWvrGvwaDD zPp`ER#wwhdu)Zv8lr3^$5!x5GZpVdi-ojv4r%o47I(YNEB@#x6sf%kNkk9J-1iC!G z8|ut+FQT@4cU=#i%az#%8r#wQ1u_FP-wt{xaA`DT3i&3P