From cd109fffd5da07933bffe25164cbe64ec2e31b0a Mon Sep 17 00:00:00 2001 From: aubrey Date: Thu, 16 Nov 2023 15:38:35 -0800 Subject: [PATCH] protos: change sync_height field in StatusResponse and StatusStreamResponse to full_sync_height and add a partial_sync_height field to support future partial sync functionality --- crates/bin/pcli/src/command/tx.rs | 2 +- crates/bin/pcli/src/main.rs | 6 +- .../proto/src/gen/penumbra.view.v1alpha1.rs | 16 ++- .../src/gen/penumbra.view.v1alpha1.serde.rs | 94 +++++++++++++----- .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 342426 -> 350421 bytes crates/view/src/service.rs | 10 +- crates/view/src/status.rs | 9 +- .../penumbra/view/v1alpha1/view.proto | 14 ++- 8 files changed, 106 insertions(+), 45 deletions(-) diff --git a/crates/bin/pcli/src/command/tx.rs b/crates/bin/pcli/src/command/tx.rs index e4b8f34bb4..21af05977c 100644 --- a/crates/bin/pcli/src/command/tx.rs +++ b/crates/bin/pcli/src/command/tx.rs @@ -534,7 +534,7 @@ impl TxCmd { .as_mut() .context("view service must be initialized")?; - let current_height = view.status(wallet_id).await?.sync_height; + let current_height = view.status(wallet_id).await?.full_sync_height; let mut client = ChainQueryServiceClient::new(channel.clone()); let current_epoch = client .epoch_by_height(EpochByHeightRequest { diff --git a/crates/bin/pcli/src/main.rs b/crates/bin/pcli/src/main.rs index 042198b8c4..a5259b5a94 100644 --- a/crates/bin/pcli/src/main.rs +++ b/crates/bin/pcli/src/main.rs @@ -60,12 +60,12 @@ impl App { eprintln!( "Scanning blocks from last sync height {} to latest height {}", - initial_status.sync_height, initial_status.latest_known_block_height, + initial_status.full_sync_height, initial_status.latest_known_block_height, ); use indicatif::{ProgressBar, ProgressDrawTarget, ProgressStyle}; let progress_bar = ProgressBar::with_draw_target( - initial_status.latest_known_block_height - initial_status.sync_height, + initial_status.latest_known_block_height - initial_status.full_sync_height, ProgressDrawTarget::stdout(), ) .with_style( @@ -75,7 +75,7 @@ impl App { progress_bar.set_position(0); while let Some(status) = status_stream.next().await.transpose()? { - progress_bar.set_position(status.sync_height - initial_status.sync_height); + progress_bar.set_position(status.full_sync_height - initial_status.full_sync_height); } progress_bar.finish(); diff --git a/crates/proto/src/gen/penumbra.view.v1alpha1.rs b/crates/proto/src/gen/penumbra.view.v1alpha1.rs index 1236a7db42..564f812cde 100644 --- a/crates/proto/src/gen/penumbra.view.v1alpha1.rs +++ b/crates/proto/src/gen/penumbra.view.v1alpha1.rs @@ -331,11 +331,14 @@ pub struct StatusRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct StatusResponse { - /// The height the view service has synchronized to so far + /// The height the view service has synchronized to so far when doing a full linear sync #[prost(uint64, tag = "1")] - pub sync_height: u64, + pub full_sync_height: u64, + /// The height the view service has synchronized to so far when doing a partial sync + #[prost(uint64, tag = "2")] + pub partial_sync_height: u64, /// Whether the view service is catching up with the chain state - #[prost(bool, tag = "2")] + #[prost(bool, tag = "3")] pub catching_up: bool, } /// Requests streaming updates on the sync height until the view service is synchronized. @@ -350,10 +353,15 @@ pub struct StatusStreamRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct StatusStreamResponse { + /// The latest known block height #[prost(uint64, tag = "1")] pub latest_known_block_height: u64, + /// The height the view service has synchronized to so far when doing a full linear sync #[prost(uint64, tag = "2")] - pub sync_height: u64, + pub full_sync_height: u64, + /// The height the view service has synchronized to so far when doing a partial sync + #[prost(uint64, tag = "3")] + pub partial_sync_height: u64, } /// A query for notes known by the view service. /// diff --git a/crates/proto/src/gen/penumbra.view.v1alpha1.serde.rs b/crates/proto/src/gen/penumbra.view.v1alpha1.serde.rs index 51d880bd01..ba13d30305 100644 --- a/crates/proto/src/gen/penumbra.view.v1alpha1.serde.rs +++ b/crates/proto/src/gen/penumbra.view.v1alpha1.serde.rs @@ -3443,16 +3443,23 @@ impl serde::Serialize for StatusResponse { { use serde::ser::SerializeStruct; let mut len = 0; - if self.sync_height != 0 { + if self.full_sync_height != 0 { + len += 1; + } + if self.partial_sync_height != 0 { len += 1; } if self.catching_up { len += 1; } let mut struct_ser = serializer.serialize_struct("penumbra.view.v1alpha1.StatusResponse", len)?; - if self.sync_height != 0 { + if self.full_sync_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("fullSyncHeight", ToString::to_string(&self.full_sync_height).as_str())?; + } + if self.partial_sync_height != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("syncHeight", ToString::to_string(&self.sync_height).as_str())?; + struct_ser.serialize_field("partialSyncHeight", ToString::to_string(&self.partial_sync_height).as_str())?; } if self.catching_up { struct_ser.serialize_field("catchingUp", &self.catching_up)?; @@ -3467,15 +3474,18 @@ impl<'de> serde::Deserialize<'de> for StatusResponse { D: serde::Deserializer<'de>, { const FIELDS: &[&str] = &[ - "sync_height", - "syncHeight", + "full_sync_height", + "fullSyncHeight", + "partial_sync_height", + "partialSyncHeight", "catching_up", "catchingUp", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { - SyncHeight, + FullSyncHeight, + PartialSyncHeight, CatchingUp, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -3498,7 +3508,8 @@ impl<'de> serde::Deserialize<'de> for StatusResponse { E: serde::de::Error, { match value { - "syncHeight" | "sync_height" => Ok(GeneratedField::SyncHeight), + "fullSyncHeight" | "full_sync_height" => Ok(GeneratedField::FullSyncHeight), + "partialSyncHeight" | "partial_sync_height" => Ok(GeneratedField::PartialSyncHeight), "catchingUp" | "catching_up" => Ok(GeneratedField::CatchingUp), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } @@ -3519,15 +3530,24 @@ impl<'de> serde::Deserialize<'de> for StatusResponse { where V: serde::de::MapAccess<'de>, { - let mut sync_height__ = None; + let mut full_sync_height__ = None; + let mut partial_sync_height__ = None; let mut catching_up__ = None; while let Some(k) = map_.next_key()? { match k { - GeneratedField::SyncHeight => { - if sync_height__.is_some() { - return Err(serde::de::Error::duplicate_field("syncHeight")); + GeneratedField::FullSyncHeight => { + if full_sync_height__.is_some() { + return Err(serde::de::Error::duplicate_field("fullSyncHeight")); + } + full_sync_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::PartialSyncHeight => { + if partial_sync_height__.is_some() { + return Err(serde::de::Error::duplicate_field("partialSyncHeight")); } - sync_height__ = + partial_sync_height__ = Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } @@ -3540,7 +3560,8 @@ impl<'de> serde::Deserialize<'de> for StatusResponse { } } Ok(StatusResponse { - sync_height: sync_height__.unwrap_or_default(), + full_sync_height: full_sync_height__.unwrap_or_default(), + partial_sync_height: partial_sync_height__.unwrap_or_default(), catching_up: catching_up__.unwrap_or_default(), }) } @@ -3651,7 +3672,10 @@ impl serde::Serialize for StatusStreamResponse { if self.latest_known_block_height != 0 { len += 1; } - if self.sync_height != 0 { + if self.full_sync_height != 0 { + len += 1; + } + if self.partial_sync_height != 0 { len += 1; } let mut struct_ser = serializer.serialize_struct("penumbra.view.v1alpha1.StatusStreamResponse", len)?; @@ -3659,9 +3683,13 @@ impl serde::Serialize for StatusStreamResponse { #[allow(clippy::needless_borrow)] struct_ser.serialize_field("latestKnownBlockHeight", ToString::to_string(&self.latest_known_block_height).as_str())?; } - if self.sync_height != 0 { + if self.full_sync_height != 0 { + #[allow(clippy::needless_borrow)] + struct_ser.serialize_field("fullSyncHeight", ToString::to_string(&self.full_sync_height).as_str())?; + } + if self.partial_sync_height != 0 { #[allow(clippy::needless_borrow)] - struct_ser.serialize_field("syncHeight", ToString::to_string(&self.sync_height).as_str())?; + struct_ser.serialize_field("partialSyncHeight", ToString::to_string(&self.partial_sync_height).as_str())?; } struct_ser.end() } @@ -3675,14 +3703,17 @@ impl<'de> serde::Deserialize<'de> for StatusStreamResponse { const FIELDS: &[&str] = &[ "latest_known_block_height", "latestKnownBlockHeight", - "sync_height", - "syncHeight", + "full_sync_height", + "fullSyncHeight", + "partial_sync_height", + "partialSyncHeight", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { LatestKnownBlockHeight, - SyncHeight, + FullSyncHeight, + PartialSyncHeight, } impl<'de> serde::Deserialize<'de> for GeneratedField { fn deserialize(deserializer: D) -> std::result::Result @@ -3705,7 +3736,8 @@ impl<'de> serde::Deserialize<'de> for StatusStreamResponse { { match value { "latestKnownBlockHeight" | "latest_known_block_height" => Ok(GeneratedField::LatestKnownBlockHeight), - "syncHeight" | "sync_height" => Ok(GeneratedField::SyncHeight), + "fullSyncHeight" | "full_sync_height" => Ok(GeneratedField::FullSyncHeight), + "partialSyncHeight" | "partial_sync_height" => Ok(GeneratedField::PartialSyncHeight), _ => Err(serde::de::Error::unknown_field(value, FIELDS)), } } @@ -3726,7 +3758,8 @@ impl<'de> serde::Deserialize<'de> for StatusStreamResponse { V: serde::de::MapAccess<'de>, { let mut latest_known_block_height__ = None; - let mut sync_height__ = None; + let mut full_sync_height__ = None; + let mut partial_sync_height__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::LatestKnownBlockHeight => { @@ -3737,11 +3770,19 @@ impl<'de> serde::Deserialize<'de> for StatusStreamResponse { Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } - GeneratedField::SyncHeight => { - if sync_height__.is_some() { - return Err(serde::de::Error::duplicate_field("syncHeight")); + GeneratedField::FullSyncHeight => { + if full_sync_height__.is_some() { + return Err(serde::de::Error::duplicate_field("fullSyncHeight")); + } + full_sync_height__ = + Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) + ; + } + GeneratedField::PartialSyncHeight => { + if partial_sync_height__.is_some() { + return Err(serde::de::Error::duplicate_field("partialSyncHeight")); } - sync_height__ = + partial_sync_height__ = Some(map_.next_value::<::pbjson::private::NumberDeserialize<_>>()?.0) ; } @@ -3749,7 +3790,8 @@ impl<'de> serde::Deserialize<'de> for StatusStreamResponse { } Ok(StatusStreamResponse { latest_known_block_height: latest_known_block_height__.unwrap_or_default(), - sync_height: sync_height__.unwrap_or_default(), + full_sync_height: full_sync_height__.unwrap_or_default(), + partial_sync_height: partial_sync_height__.unwrap_or_default(), }) } } diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index 33ac808644ac9892fddc5579b2ecdd7926e7ffcd..6e93c8f810d3457f72982816a71c39af47839074 100644 GIT binary patch delta 27558 zcmd^od3;sXx%Sy>t-VjOGmz{A2w^$|!kB>}ObXZtK|zK#puJ9)9+Cq@LXsvyi>=*A z(PFKF?$#p|arh7rv?@{Q$e;)n3Mg6;5V>HLTCJ!px71qwo_7srA8haa?*04o_tRgV zwbuKrcfIes=CzM&{#^LGr#sv3>F2o{{C#(yd-})4=fW)r;*S*sJDtu?cU;7Zy&^Z7 zsBdU*Xl^RXiCF_PYYpu96C2Oj6CxPme6y=}wVPF+sB4V3#${0!;{&sX4-alZBW8<8r_@YD+lWoeNgc-n|=Zf|v zEr}x1BGa#x!0YQ~HMGrXO|&GSL4A=xUdE-Y$f|W4+G=&GSL|lACfX9MH==}1C2CVx zvoWE4@x=O3lRLY1OsWv!~3Qkr(lD-L#hHSsiPg_p{h9d2ok!>Z+^U z{`HBL)&wk=sGrlAm>;iOGUtZ2=B7FG8WN55bLyI#<~26dwYL>zrBp8oC8kM%sVG00 zici#~gKB49aIl~^yDhWhY{6Ho2?O2i`o!GD^I?hlcwKu@9+6#S&x<#es0qCL?hV<8{Ka)*}(!;bYVubA0FX|*P^HZ12&b)EKj>-SAcqwZV$c3;dER-A-T9#p~?m*VfFgnL!qx zbLEUHX3YFLS^k3L+AEPgy(Z7{3f;W0^sJdz)=sR+V6q0oVfh0iMQ(;`Wq5pW zWRjcdvWzVCr%D_WDU*qo$LUX%SQZ&36Cp%@s>JfhXqi~*ar#pwRz&*9#C{&9KUHF7 zq_<4$?eWUa_*x#KdryJNv?5%2imr2!X@8L#<} zWFsvrUorrJZ#?3LY6b$|_-@7SU@45O3BkC5Wn(6!ge?e9NC{h1!t17mEeKEO(bKJx z!p!59}a2ACM+4w^M)VoI1pcw$zbF3ur5 zF~7thDqlLly;%ywdXt0IgT6Cna!Oc0cydY@rs9+HV_H~1cyixTca#)H!70J?!DIX> zDPbE9n37eji`x*M(tUtI5S}u4i1ebxP;e?M*I5qqn3^JSC{KG41wk^kxAHd-NTv=c z(Yt zDgH(a$HEyE*`8kav?ptyuy7{Nu$$V!+I4btw5Al`(?3`me%I> z<`E@jv3OH`tVFMi+Da;1H#TKn>>E1a8?lDA7^dSkOn_yv_60yU#v2zWVv82Hwa4Zr zE|gT^pM@4)lhQE@v2aZ)T(h9#HJ)FjmCS;Y*L3gcUM^dRq33$myZGYR>;;LKRv^|q z57j211JsSpYiLZA#co>AP`7{-i#NrZTjJk@I&JN(4Ndc*yiy?>DqNpZAsZ@OpHd;) z@_O;>Q~fO)DqP>Ampc%hpLupXa;p_2%Ln-JjIJ^W81??Sth;0e0P|eU3RC_qCKqi$Nyly3k)QlBER;Kh0!Nywv;u~HSefMBT~I7qW#I7sMtWQ>6& zkz%24lSr{peF+_pC<&5;4r&SJvmv@Y93=BoBpf93WheD*C{5$T{O6bK{>hLnL4R^bs(+`yW-s(_RIZg7j-?ye&e$Rx`z z6g^9_V^f;y8W*=U+?arebKNLON>3~l*&&867Up;%2HV1dq7b9zh248dc9KVSWAIRA zProsZghuAZG!pVgV?mb`3Av*YPR)UeL;{C2iON(qxuhwLj-1kzQxfKbuBq?f5QENd zLN8T8g34P`?zO?ylJX1Tae{hvG*pVc!XQ z4BNbDiQ841Ue2dC{}+BmCMvr*WkXr)2fKlPiDZc`v%9~SuwaBmEhF`n-_!Cs$ zPNO34emjkdy!-8xcTpZx-%fQ|4F0z;id5PxbLPs<_Qm0q77!ha^rw2;OG(9$_G=xXl_h7C{Lf zbV=v?7JJa*Dw{f`_`c*>3q)N5Mo90t5?u1?}*`O-uvcm+};|>c>N|k^DcUT48!UQOMhlM55%~~TAxXThH z#W%=_!>0}KlJajZjyGZ_4n1gdY^IS=5xJeLAc@v+HjBp^6Y+Y?ByOy}xjhE$=e0IB zwHs+-mUL+iK#WoCVFP?b8B%fzQVJu?kWkn&Oh60nvSNMQhb`R%=81bP(X)7!CEM`j zCeS|qzi-EtO^po;6K-sJ!f%h)$J^r-BvTekJ4KKwYT{lHzcCR@HCDEDUUO?~QKGFa zK0nds%0)o}9&Jl&DF!!EGHfYUqDV+IgCgjiRl1?TKeoiE;(pM~ zZ1XE(UxnG*60ybjx5S)H&{D+20A?u?3#-OD7701|VF^_IW6Oh$eH~d`Rs|!cNHVBM z$w)WP&5*Ij(N&Ssx5_C%gz6-Rg^+L4F+a;Sf7~J&;(uMWGQt@RN=;E!cwg_HNe}-WX3N4LaJFF zvcwzHT7-#_v38*DdtXT;*W6aY1t<8V|pw z_%O1BXNYYy0Kso<3z`>G!5fZMKE8g5P?r0QC!U$n{b2jSF)PPz!%QP-tIBsZju z$!LQ}yWbpZTSQgLlr{>?`H7|kPBWp{+=ixjYeRy%wVDiQ_<|);5L3mY7B(b0SK6s- ziGi@~V`150#@@`q?N}>`=%I ztSRd#rHyuiCZA1Kw8))EHOL*l*ldZy;t{cGv=Gq~#Ag@ys_kL)(BW;^2}le)kn5yE zoDwkLZnnGvr8*K&V2dU6m`zwEl`aT@wCNiitn^y{%myo|GIHf zJ4F5LnUiJ?oLf13^qA3OMvNXgYS2Y79A&mhf1{to7dN7(H^*>TYR*by%}sR}rzw0E zwX`qM9>s`4Q&zlnq3T#y`l(}NV>qm{rFzD#ScgK}tRC8N)Y_-5o`Vd8Tc5VDfK*Or zaO=}n#Uulx1jkNvuc1W;y4o`q>sCBHHnFh*o5HqOT|C&i_OdE&V`1=&l~bg|Y%)2Z zTGt>G_Ik$ZT;z_xLPmJb&#cH=7VNAVHuPs!Mi066zzL4${M=%1=(PthIDT$Lv)oy5 zzdGO%zY6+{ymI!hj5B2_xPN7wNeK}9wQ;7zDnU6@CP3oX21bpmlDD@XF%dN)aKJPU62*93ZT|V2!OTIhfuI7sU=*1)bf#x>%McUI}W)min)xvt^;!E5v5p zmFGr|apAtgVvmtSq&cBUBjwfDCkDMGxAR#nQ4kTW+@^nZnL}PP3@tP&NrVN-1 z5hSRk30Wf-BClD6w24&a>EwyurAu^z1QJ4tPLTK=Nes{ud7gMZh+j0Qgz1i~jxPFXm9 zp|MHGoeiKG*8tIHPMOUUAjnQxIDyfH(Opkj*w0BSs_C@F)EFpeZ%EKE(W3pdg~LrH zN)0(}<#+WB2Gwb+m`;J973uhPs1yyhfQE_&+qXk1s`qUx-zY_6$lIjUTAD|&NA|=S z8c?wxpcN+;?eaE{jy)+P?B{ma<|bcN0440 zb}St~)*AO=NI~nr4?~;L>hD7fH;Jm@wD$YZ>ebu*u4F*jPl9t3mX7}l0dO-J^7X!sk1-UQ%FVa)n0 zVAZ!z_$*|Ig5k5!$TW<7X5|~_;ur!D2jQP828?0LSg@!1@BT8DvSkJ+mNC8a7PN~8 zOc{dq2FqA4I^QN!Vn|!gSU=rJ+QS1Huiz9HFsrM{1=Dg?r1tO-1=Dhd=5%DbnZ$)b z?kB8c%EV{Hgr9^YF4Xx6OSu-Oxqx|w1jo66I0z`g{Aqj6gDmn03ogDq=0C_Vf0B|m z^_7R1?khl05>P1#2>ypma{oQePynXY+1UWTKE8hY7@i4M=6C~y_w;lUQto+0W?(9 zt2Z;fGIr=d7Z7Llx-ghFGaS}GNm1|6DauyHo@Kmv9gEe{nMbTS=s&GraM{#<_*)rH z!g^q&MLDuW2m7bPFxraATquETwbzjeuuTWk$0ITUigYlXhowZIN(aN1+th#p&oI$F zb&MLkaD_J!DIqBhe$S+nlAWGmI2kjfWT|J!=<;CCp;7A>LD96w$NeI-5RFa0V0w{> zBH;K1>p4VQh_>UuU=@{aqYMDFYx}*^o5pVs3r4w0YSB!>A50GbHyHpTS*EOAW#7sDvGK-EK7~P$OY3L)ZojbsAgbGMY>+{^{FdPWloSU76QE(Dp7mRn6_dF>vSsN$159a9{g%-w zs8k9zILL6Etfl~ed)3$g5Y@d(Hb~OUkx52464ZTlwSOdJ zApai;8K~(;817(6VMu`C2*cc?bV1YKU`*|xGo||hjV^S8_D0A=wY|Y`h^3gw{ckXw zV1emtU_rQUapbyNOhX#;U`$0A)xMef`LHMBN_oqM;VAI`UD#~QqzO!WU#3Avg=NU^t5U?8Pe0L z;bcfptA>*yy$$-4A-xUylSKbyE`q@IoMk-npB;I#Dx&$zct(M`A$7^^D|j&TnsXCY zaBUYPqTUr;&+>q%cLn#o;20CPNN#b&~7p-IYB($@5fC2Lwlw_gA9?AljeggD#Th z1w{LkylSjFNLGNdYj{vKXYloFLVntruL=3d|7&=j+S9wz|7-Zr;hK~dj@ZimOP2UY%WXcIhCc~;5cy^9^ znJ$OQH}PQ7+{^oJ;(C8Z2UuUYdHTOU~KC z*~MB}I$;Dx8d^@!ri_3&2SyN~-CECgjixxJ%L;wt8E4=HFtyghUdEr_>= z@kI;b?O}XHq2%^Z$tW?BlFAs!+`*ag9B#e{8e`C^d55mYhcdKm-of+Ks04_LcJP6N zwIwqo*N|}rqM{vq)Wy0YEUI^gR;NWZps6Sm73~ZggtrlZsSZPn>YaYrqD)k@Gi*^N zD%u&g2rKSgoEa@#R0L=$qJ{Uaup(M`@8WsJy|nP&#RsS*BYcQ@cJaX~Qn~=6qFsFC zMed(zzHk{%CiVty!?|nrh85rmgxDKafHx3;d1}K12~@C`_f!jgKvb}o$A)Q(Wr2S$ zA2JkApNJkO8vBE*Z@Bf^{UJS`H;DZqJsooF=X&6V1nBp3yv(728xZvSd4Dx<1A=}( zA2!mxSE-Nlk3+%8g+Cg1DCExp|DiBA=}6=d$3s#@m;?Sp_j8F9gM#Apsu30j6}ob3;Jruw9t}2Pvn5)lEAuJry$K zgW*)jKu0X6xL%&REFTP~IQD1z6oco9cR8yl{$;SP>Eht^<{yp+G!f^4>fMma1J%3S zGb4xxs(1O2a`$;fRp5#Dg9lr7k9$94C;-FzAwvNe-Vg0j0EYL;9^a8ZDfGnI;K6Tx zxB6^IP>6BpEZ29!E-M7VS+4Jd0YPw<<4zbizg$oU`7_6&=;mPN>gwRZ)`|X~xjyK1 zC$cG1Jnah9w4dnU&Ab37|YMNTG7#pw`&64F(7mgk)3kr#!! z>ZTrZo@aD%->}i3EV-h)Q*5A{lz#DfAQz2&p?oo?4OuqD^-SEJ-PD z0nrie6ncIFL`S$&V19a^jGjdcfmI?{bK?istqLokb-=2y0$K;G5_y%Hk=6mL#Bg=5 z4hB@PN>tUDTvV`1Tr$c1P*s4|-Y0_7H=XtG3oD>`=)SN5(&9d$M~W=DNVrenXhpSv z776!>-a`#U1^0=;mD2E8F#P?36-dKpk>T$bS=p+jG<*pCOmdmTH2i1LKzqN)RYy!f zUxKnMa#dN0a6m3KlB?zWMF>sqLyoj5?DM3+BeZ***o>L8Yc7gSYK}EE2Q}YW)$ziT z8(EEN7OiVh%s7?SHBW{np>@rZB2R4=vgEqvNrATj{Sa-`=%<6?Tl$wh9X6H((jjz` zu(uZ28Z_S0$KM(X(6S~SLO{Yl(&{dY_Ac86`)cuV9;<1J&uvt9nChW=YylqAHPQtg zUad`?HFJi(2$U}nQZLslV(QYbvH7M%YaQM1xxTz`m$#2?vBgdGiPpBdW?W&#@cdwr zydaV{ifwpthbzE@ydB0p9-g_=^`3glhs&bIC3MY%%c!QrO(=?6{s!FYx5pP^qxcQ^ zh@LdTo4zL8GsWxbniu131HJ$A<1HL8(Z8sCmQME^@2M}s)Zi@;jUg@ z>&9d4xSVQkjkhkr`+<1dVyG9NA8%-)_kpd=3vp*XcX2~wJuasg zC@;C)!DZE*{H z-!5NnG|zKm*VfSq_V|f2YbRYVBTFaDnq50(#%0&X29opjW#=F`1^vIN+`J4>y`>GE z2kxO4FC}xA;Mqk9g-QE@V4I&4yyy?eabwM3L0qFqTX5Fz<3mg3ALtC59TfU(1`tg; zDD>ewAZ&I}V0TYL4Iun;P;^(DBtVRy2SxufxC_mvJ<1`$-V|Jwmba0I1TIN>V+(?8 zvd=3*bSe&3F74^#FZEtU=D^6Wh@4#aKCa6`kE2556yL^W2UX7+E;QGoxa?S}D@yCe z|Al$vl{s4O8tYWVrr@QLf8iq)SZv~uA@pJw=EiFv^TGffx2;`{EGQ9=O>AzhcVXT( z^oDw3MnUzGHWr(V?ubk6CF-exPH)qXfJs-3Pm6Iqy$Btwb&2m{tcuU4$dFGZq*hc4 zU({f%X~bZMuHN1bzqKS<=^aCy?zZKdmX>&H8x5Lru#3A=tFjoXzaW>MlYyuqF3Tq5 z*jtb1%k(TK5%Y-zF-a}L-S`qpY{E@Bm6rvYTN~!%@k1j$FN(kv&NqeW0RH- zr_I=bGE_+9sf!^%#O7&=%{8>h$)-xrcI;cWmicEx3OXx0EA(jw%7Egmz=v%rp6L|f ztiaK)h6wMoqNm#QqcFnztQa^3*0nY_xw+|^fmtKta;^)$f0iO?++&*N$`SfDc((_d8iyYZ- zG-9>Qh7^C4)3N{d?Y!d$KX{A{m=2c?A3A(^8N%`ACHl1mO;|LU;EA4miqJeSxc08T zel^XCc&3iGG&F!v=wh(Pdn-JosEgAhGm^rr&`M7$O!Q$iqsLcz;Lm2X4uKwKV4-!$ zYFjTZkqc|Bw)M>$pi~++Z?OCTIFAlkR@>bMYfo^PezlEzhbL+AmraX^b#}1i?x(I> z7uG;i%et@znp)P``T__ds9~Lr4Tkb9O)cweeE^4CRItv*3E3Z11)y4Q2e++!@7ncY z1#|?rKCFP|ll8V2Xy{%W5fT&=-jn(BPvH=Lgjdt+Ts;~GPLw-8N z*ckHDDaOXoM0BgS(eA2NZrRKM|3OH#O74EP619XX6-Je>qdPG(O2C-CJ;4w6*rsKkAbnvnzY&9LcY_ao9AEbkqEjG?> zl-uawWs6;+_6TUDY-agz_eJS8JPC20ve`2>FMdW|2jd2qX4x-)oTtWhIfwX7Dz4G= zX1uYWrl;17^05~!31>9pYf~C2X;~U?T1;y-bfytysrj4MUh?Y>ET%9sU`06}FB{wB z6kgwqen#e9l4y6;iU~`r1V+)i_LxsEnnqQP96O?F%-BK3NVs12#8Y-uy%fk6_!sGQBQMo02i)&}xMoK=ITr*V-3`vz#ok49C>fY(h7I*%@w=-?`AorkGXd z#Tqc((=GwQQ5N%Qo|EI53SweGEX-?7B*2tfq+=@7OAf5L=rQU-ylPEpv!vpG7b3J| z&@tlwiy--OjQr~mp%L^~cC@Q@xO~C>Yg_ct)3AD{js*3xCYv6v!+XEBaj~Z6Wlrw( zw}%c#0#cA*9F7EtY`5WXBLcj`7X4C{5C;-^hY7L-?iq~)9KADLEz%naS}h-Xafs|p z*9(EPD-2yO?^2LpLKg{;*cC=Dr>(&A=@K+HBO#QqA@O{=1fuf=G~G`rPQxuy!nV`n zuNTtWj=^SkdQ)krLW1d(oJAqAo2CHMc65*z)7wsmy+{aMlHo(-#q_pg55C71-BW7O zaRL(5RWf;z-hAz`vCdI_5anO8_0eoLeH};8^g()9@sh2tN0AGzm+T(u2Z&oZfxzGUMTMb(5u;C(hz2f5kkv=W+{@F^W~Q#H{c@IG5##G){m z_Srb(q;D;<>5%t;EjMnngBS0gRE-p{A<3|1f$e~;cW%f9+W}ke+yKFLz}7oAI=y{4 zcxvs}hiB8q?PXhU+<+hjs5WkZu-nTvHf}wz-^+&C4%@-E?(KQ`VOt;UWXpq{bO;Fv zX{(pxIlr?b$Msf^4sm~HW2;9E%AwKuNU;6>E2{A)T5`m+1Q0DbVp;+Sxg(|}fN04P z(-Jx-K1MAGb_Vw4$4opz-7socW>P$v1IKF&z^QD8Dv~wC-d;IHjnmU^-i@=3xRTl(=mp=}@ zOp)-htq*FD3on0ck5w0}fbjCi_Qf;g$U#tMkDD&LM=llqi|iiw6Se#$tc7mf|6;la za#71)O!ojJLDN0x7UZwgJ!a%mNB^toA3%@-RQ&@GG4fZ_KRS8Nr*`CaN1f2ny~n3^ zMv?nh3hz$TLB0qUCjWNr7e*zdp`I^H!vP`kg$Zv!sPu&iZ$LEs3lrXe2=6Z_ylGhI zL^fOI;G43qj$7tv!y*w)Ugqer4Nw-+V;dm&mpOWDqg$KX96FVn*@@bGo1+IeAcz5~ zHUpx~w>f%D$n%^PPGpU4GhNlJaB%p4luo7csLg@X@zLsI*0q7r3aO|naP;;J5JG{Y z_cnmgDsc228z5R8IQota5Umaz+_6!s^QhJLQLCp0&1;=;_l0#(%kMKSM>$zgw;T|4 z+-F)&*G;Rb<=0MKZ5o{?8x5!$4TwgsHjU2roV8Bm=ep5!yR_EH=3OGVCq}>oSQ^4HDEMKqjCaPdHeY^}}%hC7{4honZSzrG0C)95t;+#F=>*QhWBex_{VJTw(IEMxBcI(<+i_6ww8OSK>Y)KJx*!r%M+bygo_6%i z4T#F0c6z8YJwO=YX-7YN0E7{?I*z&x1%wf{I{E#wrR(#l;%BAnFU8-e=Gm|sI(d24 zI3KyF=2=HO9}v|%Yn)Fvg+G&FKQf=f{%0lvfFK5_A^;E-{){5vPL8?I^PF8y`%BRk+K+YsvjIYk2gWaIeGmIUTpGpJMrY9ap_SM+SN4H;yQg>n(|)HWJk1KA8Y* ze>`^kf2b_>H4KN0bRr{x=TZ8PT$j5>VhJNAyi`lEsmrIq3r{nnPcrcF-Z36p=D` zSCdKr=}Qh?*3jb1BQ^IsOucLZ0wJJsAs}qK-^nVF>o`Dg?RW4MAua0w!L;AOsg`^( z?a6cSLr(D0h6nsZAqQ!6$k8Wbo(zLSj=tap1jiuF;!;Y`tF?sTI=x}Il6n@oVYMl&&feO%2(J|1gp;ENf zc-6@_uAys%S1A~jQt-@cj()l4F@y&}!&F5lKCgvZ(H7)2$5V$LC=IID96a4SW+A!@ zJm*a(av~aZee8|=H)%j$mXFXVpwL(m-MaaE8FN-dH8m1Z@rtMp2|zHfh!(0-6+ker zh~kYMozxdl_qZd<-i)e~dSH~kBZ^Lf|6l-8z2xC&$LXy<;Qqrn^_5X7)6sy3qxw0m zOn~Ua(E>HN$plpOa1?_Z1-4AU5b|)e#}N4$sY*aNJsj;h*ged2bs*Uk6=lV{7`{`x zDZYd*n%eNO9)4=7||)Q?3AOQnb}IV1^w%EFL><9JjIPmxl@BOxTEm_MFQN>P72T38;6fb)2C z=rFoeXQI$^PDkm7jEYZMY0pD(b@c3xY# zp84;Y7Rgk!=sn|UnE=7}jHhJ+Dtga&S|-5L?-@_41UU9R^7Kqy7dSo$nxFsLqz}^T zqNw`7SdRoLsy>Jos-q5>fVw`2;;4f@R-*(Ee-wxpmehQdPDJtYQM%F;FCRtqlXEEt zqK~2&p^uy9BjH@|{0sGB?hT2$VDRq6{<(BY3YT-~loT%KqWZx(QPS9WE{aov888sr%*&cAS?MfJ50Xfwx?IPIbX-bAA>FD>cZN}m1e z%OE#a5&p1@z7j(1@~ZD|(ad~zGCmcT3FOefN1aZ^Rdj#XimPin6HQe^r>Ce!or%gb z)Oz(qn_dWjIN!&oADReF1ryT3BpCkhQCt^}OI3$L|A;!d#e?N_AwKJ-n>GDW5M7PQ zD^U3f^`gb}B`tmO9TJkOA)Xl~`SK|2A5pxZ9FwX9)qWAhg0X)LS0?(ZOx@3?Rfu1| zTil9oxG$)HLI9#!VFhy4_C*vgL#Tg~3(=i#&Csie!b>SZrKk=rOQ6r)nvtVUxnu%p zZ_VhURuM7*VQ^~(RuQY~++_ZZR_BhhyYF7ZPL?PAIo65H^F!#z%X&o~+Lri+Rqyx{ zKET!EhloTNVcoq`Kkx(*#UD0z2-)d$~e|8vfuixuB3cnFc)(O_+JV>RE3(`|gL zD%rBY>b-5<4c4EmEct}Ojg7}Y+wN_&DtIS9Bg;MFV%(CW(%Nl@nystt^0gMrkYgQE zVfD2ZqMg=TfUx;mtFW{Cbt!|)2a{JWwyy5|pv9uP3l>M3JcvJ&f1GFa z%C0n?DveYo3+G!yN|h4^hEAYAkM)#JaF$z7j6mx%X5ON~mtJVA8Z zcH}E!gWc_h=K6R!J^QF=YhK)1M=xve(yyRwB6`P07Fv$)+ua9f zvn1`fB026d@n8jh1Ju}r3azj*)Wa!d#ufBgtUQX6i|V_RnU{+*z3&cX$c}ekXve#^ zb($h37uXN=w*K(&wrj2vpXMg7ZxG#*6BdY^Z8tB(?-)qDZP+65gq5-4|MZG%THnh` zzS|@Yr_i>Y&0^1G>=!Gxt=J~sDy+NHa&zfNM;GHqKHFNFad&{{hI#ZeBXio8G}X;n zkZ72{pk4mDsHn}Q^jS#0T&8*zZUHVy=@#sQ!VEucDQfd|)~w{p-->P)Pd@fr(TFeQ z-NNM1-;2Uz*#R-`tN*GlCH_CGi(gpRHvfQ_-Y4t){T78(B(hEOqc5|nCnyP8seuUxC*;UE4UG34=shgGhX7wuy@&{e;U@H-CjgcMH!WKf9pCF|= zh*}Ih!ZOv>rCJO;!tm&fPALm%C|u82pW<3s!>kKy(AK$ton~f87#o?g6t$P>vGMRtAO zX8P=IILJDaF&<~hnS<@}DgcbWk2Ad|0|ejWtd}}W2L#{aY;c)d0lqGt^AwA0OJ3j2 zF880J4-U)0*M<1DGB%*Nx16OKF%RL@*TN<|vx?1aY_3}vQ*outisEPi%BzM0qQ2M~q4Ss=Hm8ys^7oSdIj>f$J)l zdC#+6>a+w9!p}2&ol+--(a0B*+q&C{iWkvV89QC&2H-_jq8_pWLgYn;hpek;wChT6 zUvg>>``(IujH&xjBtngStovY1LF=`B3_l-6f!3Ag{sTJD01#*gLIr6Rf4~G9av^zu z0&OzPROC6Yu*k9G!k%_br&s8UF8UqJA~Msf$sIlIQN!^imohjKLG&uiR2l(-=v9W> zD>73NdE<447X;c&SM{_-l6kKfGGP1Hn7U%g7pt<^cdGCQ+4;8_gUmaP1_ws-#1?|b|=~o7@H~ufj|qWcmPrM z12$5ih7wI}cIV-L$a$8f%j4Wke}iT!-?XdnBY^7CdGKq6Srv#4570D|>A!-9go;p<85 z{4|*?u`e6QdM3NkhR>p6t=j z9$B%J>)8vbkXg#{K!nzufFNGVvE92!%3xq!#*=i0 zu$M|1w0t>F{-K{ev2uARLq}4}dDIw$j--~;$6Dl>UY@gpM}EMQclEKyOzVY`E9fHw z@^>%t$Q_)GmihsK9Z((40ix_3yq|hm2ME?X_{dTC9aAQH+a(6%X}tc zp0koi?%`@wh(UNI*B`6KsG?Pz4U-iCp&~#vN&%wmDqf;K!UaS{t9a#5Odd@1@x+gk zclNg@*Zqj&N{ISYiRawMBlqiyN+5h6$EgRksDvuQ_lT-RK&S{%byYx=P4a=NMS!R% zXYkiAz^?RvVw%;@a~|N42X$@z!0>=+RzIq3o%x6i2(qR$)JAw>HE*OL?wXaQB%1Vq^vINt5jPzeay7kJf3 z*{g?;xp#B@n;$?JIRVuk0Z`_uQML7ttu@E36_ZSNSLU509 zW0~i?#3TE4Q_4{CB@=>W)RcXksj~zi=mV(q0Yuq-98c(}DS%+z$2;Y@rR2wQ&pE&& zFKgOz6g|K*`bjsI6YZhoMdfzY^h2DfFQAZ!GKc8HSfT?2-64+q24btgPX+ME5zSVC zl84FOmmw%Bi0w7bii^i4hgI19lc&n|PzEBeb6j0qlQRgNdOSJ*Sl zj~g~5qRergsb+paupQ@^`OBmTGEXEgs`sSwg80439^L}fu~O?|A5kSuGt_h5<&i&V(+ow)cTFe_CDWWS z4Fp1f0;A_0A9>^hO*;%l|Hv~aE`%6HwD^)z#Sjo^ z0TnGE%Hm5(MGFYp_YLiE&-su?KGLkiQSw8MB^5)Zcpum4==mRWmy#|^J#M22zy-l zr)F9~B8WaU(+VJnKBZ}e{_h7>q{C;)M@QIY<)3l=TOvrLR9>z|G(d=aMkCtwQUtO3 zdGhlS_MD2(%|fV3#_H!>Z`S}J@;S$LtyGEtEfvW-tL*+2ONAadt7Nb)r4Jq`L;z(e zR73w$hboHIWg>a9%D$*#8Gijq7ouQYCNjGk2$^L9w}13Bq>48D%SH0Ck@i>1mxnU6 zE?qA4aDhUQSuQYKlt>wv{r2SUk#@iG+d~mrmfkM(k!zK_DZO1_KBL$5RrL6MMY8iK zyV_qN^z&pSLS%&)Vp>BF%U1|F4KOj%bM6$8Kz}Yf5+(1%2LduON0Qfmh|kPqi-4dH zpjs3FqU;ZaUNQlK^@jr9qo5wieB|CG*hno)Aq1$lny?p$nG>s>6|y^gpTq)UE@B=)J~uqde!wBCX%O*M6U4yB{s$uD|4~j`dwCQ@$2%_BB(gtNgcOo&VS{kl6)095;hdLro4u9fET zt}p+_y2fdEyL*+D=pJd~rnpv$$BQDv+*FsP#MDn!SR5&qg_g(Zrz$Lol*>X0(N9%a z8tEqs3q4LhRbg4Aqb%&~ar&tWdq(nQVZO(Ew#08$S`lgQCeVGV$1D6-i|*(BBLn@x z#Z#~EpVHQixk5x(#Nz{4H#g0-2?#>2K@L!?gF#StKz=8KI8c|aZkfx3<%z+O34T*? zS^D6VcJ2W1TEsiV?^kkJ{~?+WMK;P}S&{(=d_%le27zx#+g!JY6h_tM{??M>{+B0( zEeKzp6t<{^*H#N#5Wc*9o?9V>na79v>7{-94^0X)2oFsPGYAj$+G}A3;i36)_vca= zV3uL~$NA@U^qi8WS9K}j>l>NXANUAY#Wd6>Jtu1MG9li z-o!fO{>-oIb>+BtWzD2`!Hk*H>!$ZEC{;7WVlgOoQ<6G{A-pLWWie3SU>Y8fw7V+{zF+q-qwESNx*dIT9`V5v;9P@q*NS*X8C zJ%VTnl1d#05=>-8+8z#)iAfR;l8G{`6E>8l88xw87t<6h6TA1&sz7d%j%#Qlk@7td zSSIPX1_H?>9oIl0nN(B^N_d4wJaH$xo2w2u`R`7*joZ$3L<%a&^LP1~{aat7^50z| z%}sZsBqu$8R|_x1ki9FrO^DIYUG3USE^3eJ$^NwdtrPeOWhXbIqWm?v85NDdNRNQ$<;_nucL&GddcGsVxe^deBYn)I($RPopsvv8)0EomVG^)CN~| zax^H(RO<=B7$uqNTs0|yKvJC?QZ%e#WqO<_$-AXf?Vn)nj_wwAwtoXRfn!Bpv)9;MW ztg5Y=c~8}(c%5I@edUQ{6NYsjSPjYmD(d9kK(Y0uF zXJd5p60@6;(74WSMndB{J2|cp0?F*;1VwV3!zeB(@F_XYNg7YdaZXI-3)F#TPFt1i zfj}|`$$p+R-e$;-bN!3~_uZ_BC_m0k5~V_;xyf`v`EjnNaxRnkac)6pLvX|76icF0!jlq1TpV4#6Ub?<@Yv_6YM*{oj{zn zC+1uJ$wz)s!EI2^w_3DO?QBd!f>{P4CC;~6wsCtyFX1_htjKZ8k6%^ZbCH$O9ziYe zpNQuyw%F4amqNhcSZqaO?)8{5YAYj_`VU++{)VNN$Z#_-hh-_ams(ynSX48JJz|OM zWCM&C6{Lp+ka)z(^4!^40=VAqF>KBa_05}Mii&z_W{6??a!ceSc_>Ix5URo`Bvv*jhc8!IqGggC6O{@w!vaXGva)lM>R_OP<`P)?sUVba zAQ2=b64?JmJ+T^zE6Ir|NChpz0*I`(GP9DMqjN!JtW{mS8G1?!ue;iB1k zE4!uJNjHmmVx#}U@WRB#=4>%E+emC?xY9iFWOM(dL3I>_s;5EXNy`g8+QJi?nnz9x zNT48ePYXzFvNCl@+zpSWd*Ufev`#uO9U>^m3JW0el$ApZfeOzIPyC^|LCxP}hR&1q(YT4KZC`u#lN0 z74_6$vG%$WeG)WGNb-Q##w$@Wq`}H+oiG?w4OTAg-k=rf_+qFOR&IiZ3JW)2l8X9$ z(aJJPVbunNQtK$wW1aKFD;8^;yVQ!0xHDciXGYa+HI>t<;!|qlwN-U~{-}orquy|1 zV}<6i46PRBpI5BR90Mg;>o#s9libkj&4w>W4X_5i8B*Z9BHj#rMrXS>EzdX@YX)G4 zeD}AK0d05sU4HgsVqZv+0*Za15|r2Wg^s|24A_BAax_dxY5OhKS!;;pn4lRIoL2y2txYYM z_FHX?IXJNZ!kmUwH(EYT3GTcheeFKUmoPDy1;1 zf3$EURa7*q0Sy(+>K`o}N_8!mezb5Z)euZSS~!+&qo{XqAaNdKYysoBPcXB6^%qR& z>~H?%Pl*Q^cCGD^Y0-{6@emVv1!?gSH5036)lRvm3WrbH+Q?ccf$Frd0KR#MVKf8q@j6QfhPV zP}W-F-#g*iK}%TDLzJ7Au$Y>IXabHUEU!p=h?58_Epw}70Kl$g{_Vf28N4hcpv<(4 z>1^)ETc>4AX9YkIEMo|(a)edXb9@$A?Z4<_e}43m|j?s74SF^eYTK zAoN?o^kM-B`W0j$#X^+AY9-T)1rV434HNlmCDR!`foj=0OU-{kP_1Nov53-Q5inf9 zQKSHY3eZqdSOqMm(l2Vk6tMOx1_8koFkHfQhRrFS^B9Y4U{-Fc_^_(li8H6%QB@nS z^atFMop_9;&;^OWc^5udXM6yJ?f_MHfY5xM@c|&ZTW5R#i0;-IAEb~jk29QQNO2%A z0U9QnT930>T-GK~En9c&YFdNpafSnpQVKp;&u}JGROACdLq$GV&u}W#wP0G$a4ggi zOzRoWg+1YeRL^;mMg9mM6#Ne#jJ!2}!jlZA8|p5Ve6Y#*00`Xys_p>cgH6T021C8%beEfME;6;-hqd=}$9OpexCA zK%)z;Edh1Nr^?}fnt6GqF_@laaXKK2pMx3j8O92AEv{S$8Y-H8&oCS>l@>Jpo?&g( z_&`+3cIxb2EvknLVV8oBIhUIr}wtjUqaRWujS=L=8e3`ERiwEl-k+K2%4RS}533Sx|fw7Cs|8n|B?*|smc86g>L^%y& z6Y~enxVlpl=DS>hWHG$j#L`;0!*x4!eu4R$W?b9p0@DXHYDXuI3k>I1(+>D2=4br+ z+Qd&xAC0LUrRAR(4%0MCT^gKAj9sRc#r+vUHMLNQsdb6z4G$o?zQo$7sQ?JOFR`v> z=qPkAg+oW7dx?hbpsQR9t_2NW{(6!%cmYrLKpWx(T;~Co?(hM1Mgc?*3wVA<_wVo! zlm+F=h6ieY!4fM&3fd8`4%Fw=f70*;z2@oBv;@x^^Po_w&qCp0tqgA}`Wx6Bmsso<%I_;_f zO-H!p0!$i&Yc9Z~LA0w55}`$@=qLy+N<~LOXc4yDt2r}TxabJbbVNJv)nP}p^Ipv} z&G6FBdo}N>_KX-q^s}1xP?6FK7#*$VeFnPU(|X}DTunUTPn~^s!V_T!_-r7a2s^-c z1HepmVuA!Zc!KAtojxEsc!I~vwZ~%Me}WhF#8*C|$CbuLf5*KK{Ay!Jp9cDkAwAu4 zY~(s|Ljv?0IX*&B;syl$M&3mwZa~m)NbI9KU{F}qzq&tz# z+%uW01^749IE;`A;yPrjUv=N8!COOubP#L}3FwAoEBDlr=(2PWY~}dGCQpnm!}ZAa zhU52TTZ6WT6d9n{9#YVi$#$O1;u)aW&O3IJr&gEYy5vQF(A@h6zZeo^g5brFAQJ>H zhB{<|;6>8m4dn*hr0np2nETey9U(&&7+Pw_vcRx|Wz@f1JxVcGa1AK)f>F1#C=CmW#dHcU;6F4gLZ}t*swxz<$&Nlu5Wn(LGT{OE$>1L z*Ss`A-uHLhzbNrO*B895yx;{?cQ1g*r|)xI@Wx006Z-?s($zFiMN0pG$6BZeOr;gL zIfS5uX;?F2p0k%nd|^o%nkajDN-Osh8+OFxM(%6g@aG3UWjB9qOhGZ!``W}mAf&$L znQA8r2vff1*ojj710w#v=6YEIMErlvu`GQ{zK+rQe}wx@4;{PtNZ0`_|3|_OX!$?F zGs`q1E&oS&FLgl<26S+QS6pps(ZLaZ`7rl{>HyZBb4ruu~8FoNgoHPj{ zCiey>Ij&Tc1++If$@6;}hz?Hj9%ag7aQrFGvZdo=VHN<$_wW&IfC z*Hb)QT`d9qFDTohR<)G~7voYRxk*07LuhI@IMS!^&%*-WS?itnjgzbDCRfdjzD&^fk}L6Bf~2On<3OFBNvH+S zGNES@AT(Vj^!E~42$uUN9=Un!a-k zSu-bAx&B9wcIELYb-q*IF#&nyaj?OTwL+h20nyo7q3SRG=8p@Jk(6_+qfTFH zn7VQuOS}|P(2@5gq4#@egMMBTcuJt0ONW7%1g>y2gs)!`d1~*D#_;t^qFcH4HSY3X z7VOQWuW91GETS#sSAjHjm;VYvHrJoOy08Gh)W-~21KnN`*eiFJ4wCozulet{VVI z_xEk+<)?1QPTVjV_uSJe=ftb4D)Ci!dVJ!{%G$}e)|hgqe2+1EGUB$f24Q?pW%ZOv z@jK?!RmE%TW>!^B!wtC`zjI1WWp(wO_>9V#wTSjvGp3{Y#EDh4wYWr!-!OLA*lu@} z^(rqP(6euOdGGE6Xcs&34QgRCKJ*jfva~diy=EC+V(IILv~2u-f@4hz;V*Z=c|=WfO?(+3@} z4u^eEDmg6l9WbQe(Zd2;4Alo6!4C_41V=6UI4p2RKCJox)d}H$@cSdTod`RiL->iX z13Jl^5PBz?CQmXagkER>(ZLC!7aBlxa6(|Axm?`VLP^T@t8`tfJ%#Cel}(_{t)R2di$fqb4aaC@v3X;@sE z1Zz={f!7Dqrqkntsq~eG9;sj`X^CwziwF?9Zw|pEA?5NGp7WE4JZP)EO%83Nf1+eg z3rq{jh?nF7GZepJ;w57uAWXbuOaz4BC1WBWOuS@Fq^L+qhP_9h#++W{doPKYPdEMYi@j%FxduTd&T5kXU4A zs#{DzsI|>!_jl35Tx;b))RO z!qzc`TF|Vp^@@pART^PIypQr4hMo5pmI1M`U{N1GdyRl9r=THIGtwJ z+9_?^|B$~j$m8q%^MCwd!g`|<%Fxkz<1avntT+Awgih;?zX0K{^~PU-@Yj0s7a5*G z=4_OP<0lN?78I`r;Oo15Cc?(1H$m9jNzG{ zv&D|Qstu=8*cKZb{7>oTERzgxup55!;%C-`2BQ^9(N%*n9S}kd#&kev)nM!6GayWF zu=Vj75T-ZSI6jlTYL_t`5T@@krUSzCU1a)q6jfQYDSO{$AKHG~S5FLk-;TDH`y!N6-+OG)UT&Nu zMmH!>ZyIC)?AT-Di$-T`6sQ0VKCt~!uN8Lsz}63KWHH)(V7F@R_S5Zv|7QFCYyA^{ zv-P_VYDaVLZ#E7Lv{T3eo3qzuAKB_A5g0>*0=0|)Vrcf-`Wp!#I^S!zS6fd&IAO1? zZ#DtpgpX`TU3UP&2_M;6ontZpvZ&+z6afB)*PTK8!#-%r?>EDbX6R$T8GbsD>X9y?sK%zl0$I!phF4Lp0 zg@qyif$n zAF=TjfwsZa0u7GZqEoT~xf=zVGeIptdX(&-?|)K6%HX~wSpd?bHtx-774%5WV>VOY zEr37>sKyTvzCC8gvgN)75M0OX9JN&f1k*7an_c-J!jreF-`V~z-hMpsUC2QieP`

yLLppaL{hbVfaG>k|`dL3P^BQWs-@QWhWYlu{7=-qv52J$Z%%G)%Pd`#w~P zHh$mR34OnX)}Z=6^fnrwv6;F*gTufKXsBq5cP3Pdws>dkEHgB8S$&3rK`DiiIcw|h zavr1Y8=zsTp#9s~P%GNMowYsnVGgZ9b=Jm*xz8*_cQ$r6cI2YtZ`%1u)=!kv=Vf6q zvMDs4b{dwx`(Vm#Pdl0$rRe!-N5=#psGoLn)QJZW)K5G3ghu%xo94$>hyCQJ<0LRz zZ*?$B7DW-#z|_e*PQ&y)i+SQ5ob_aw%5qrnj-x-9%L0hL<7BJUCJWHjI}TDCMYb$J z0(r-2U*u-GDY^jh^p2C)!~KZq?m)8J5yiQ0v-p+Ovn%J+#wS^em^#GbVC8=fNocBF8WP>{!Nw zQT&0+y$FvS8vF%*@#pv5x*)3K3uTC}1<_bbnR5Waydc_2ear*|?}8{kW>S3RP<$-uR7aOAKu3$CI=W;5 z26k~&N0%zVI4+K2V8`mdz_HZ7@ZUclwzPR)6hup-dQ?e}f@oc(b4rSiRZ;zZgeWN&u8QJPWX(i3D4Qi(HYC1!WG(x&ILMf5eUW;)2mQ5G zhsfiNQ?C<~oQ{{7zlkb{T&DlfsZ-=CS%|;cz|Y|14WdI@cUgiL*M*VpLEG`7DDzw< z;w0kPb0d#iR_@`<#(zF&O=p%He7L}BY_hT1f zAP0F(<2S#vZn8_(SS&?`4oYFm8Y@+;>430gjg`~Vy-~`ba$Ruua_gr2brz`U7_5e7 zot2_4ya6GyZsDQj7XHT?4=v{py~=l$yTS1nL_AnLMtq$5=Q3sga%F$w|Fl2(=U<(Y zfBn@tGQv28eyTqSD-8emjDD)ZE|D_hEc&SmyG9CRA^wPleyYN5k&f+UX=jh)w|gK@ zy3|={MV7;-e`}mFUfdH64%{q`HtxDb{KalHd-m+osnaJ_mQJ5}R~h{%shcvbD%d(v z40Z~84a0wfBNN3K`g=4kuL2iZDOv90v@4ZPUlLq5Nvtnj5;~B4zQjsVchSn{OX!8M zJWs-*h|i#1mH2mleJDfoq5gk5y}q&Io#L8o`}g_Q;SG(|b3{{m#=!&m(V^ATC*D0` z*n0=^BaO2j7B5-E!% zAgB8{H~sp$%DP##94`&%)lV{GS2i=rbESuKJGuCiJ+)~4A#yID^Kn=0(-8P`w- zqx`gy;nhn2;I004zhHJByF)wmDpV!AjSN>)qh(XLX;W}{UwdRPN-203I;amY zfFRn+dK9~*AZq0~&*K44@Mu4~B=J1cPZwJe(Kg1E2oN*_R8tTTgxgrE+VB8^a2wOR zy;c+rcxIxmk$@lyXhg}V?JQOK0<{p`PQKWV1tmqa_MAo*c`q0}z}}Y8$Z#1m3}&?^ zZFU6iK>M1K9mYQ>1l0}}RaAhW+Cfw_0M|aM z=;mG4u7?~sKx)MB=QuP*t!ewai^iz5KYn9b+!g9a3+^s6QmBRCE*hz8Fj8$i=dUdC zcRaP~AN+iPogMsXkX@SbS9)Yce}&nGoU|us8f5n^*<<{TLNM-OsY)|IFz#Wv#~~-R zAt!wp+%gzW`p{U6LMk;D1486OvY51QLyOoxsr{-UwsY-1qdO`fw$JDe2(f*nJ6*N4 z^_+jO$Ul+zDdn_9%YQKJNQZ#2E$RJ9aNQ95iYq@cPdrcv$9%$K>K!Q{L_T3mtP?bYD)$k zkOn328@(PdCIN!@fH4UWf(MLAxt{YCiyYD><)Ye-6~8iSP!y({oMq4s-5S2!y<<@Z96pm#yll#N3{Q9Y@`$f0xh880Yuw>u|DcM zCm?A5#jYJ8HEKte{F|}sv?!h60o9`eRLa^!zY(Syvj1kI$H=j3?>R?V9yyQV;ao6&sC|9Oxv*2>J;$PIzlt^x zIY+PK#>q~B&IkL4+BcM*4@D>moM%z>j3iGcf%EiSmL_W+C4nXu+<2AUr?e@Qq50p$ zqUzHX8iBZp-aZbLGRUMCSn%pq_GP6PLK!+xUSLsmRLhg!3@*@X$DvXNmS1E+r(yP0 zr58gPIyqisQR5IgIbNg}Q#3LiJZBz{JQVD@!X7ZX0~*ewFa9*#9cVxv z)%h9_Z6D;F)n_q4kUq%!^mVTVX+Ada!SrkFc0t9}cG={GTrMP@-iUWF6IU5H7p={Tg=OPV!>ddBmSCF|ZT9 z1e1Xy@dQlU*4c9c9$BM1>I~t)*w&dk!iy*Mqzecg0jeuBK(t-WOZ!X90nyQFe%T?%n{=cpdN3$JBy$9q&KD9S_qBDZ-x! z>=E|B;Q8TpUMZeSsf`F~sEX^T1cclZ98pONLLtqU4Lm5L{R04c+`#qD0}zrMxZZgH zLUIGwYeE;#d6Gw-(uQQny z>N%TvgGmfMVHx{(QZhNUtD z5Lf}#ixNPz-EQ6h0fKcq$JwYGd8WJPH1NpFnzlQdHc&j$D0U~>mpCg9%KOxQ<7X#d3Z@&yE1Ks8MP(e_Uq-{a}<2MF3f@rpijh8L0F z@$gH183RI21yth+C~Na{r~^ax4KtucG@x(k0R=#>Zy6T>Li8=;B0xyKWd^j^b9VB` zpS3N;Xt~qGWHH(DE@$dE3j~D#l|q1M`!2^r4YCCgtnc!SOt+9cU*b8tc;qjdwggRg z@s!STKud^rcQ9+TU2*Mh&eT&S6r#;;dbmn-fS}vWabrkqrT84dBYQPlDO$cy4j+#2 zC?&QJf>WdI5k2vYOzq`Sh&CVaR5dLD!S(@f(-sMzL{Rx5XKjM?Un1*gjImn;%g5OH z*P_hGlea(Qsp=jY0+9cZ<6{MBQ|3AQc;tZArVK6jacr-~fVzyd*&qBc#vWU;->{(& zZT9n2wJ!h!+kTFHL9rA;<;TIS>+I5!kBvLZLHmuN?d3U#dE}^O?S+&jp^U{@e->G8Z`RSm_gQ1zm4m#BjLbeXY72zkM*%?}K($c-MB4>I@1p?0 zx?V% zVPiiaL?0&mRkuKk1TBdPSl)-xUmpRhH4zZ9i|CCS`2|q6)@x!P8rH>v4b_!&Cjn^M z(&#Q0z0_6}Z6Uf?TydrD7Ah_g`tS1kF#6V&05&SqC@&E@GEfcKB?6J5R7O9)lPXJ{ z@DG|0R72NCj`r`&&;dg9cV_7Ndd@NtS)qrnFFIK!um>Im|M#WqX52>B-)N61sW*P_ zE61!}6sw&vAn5AF;30CY>Pw#WgV-;zd-sLjb)t|;MGv?X`T;`37Xt=Dc_#XK&MJWm xKy_~EhnA~^-n{ms;0c62{R6=+z;2QT5N!j|U7gwi!5WBu{qJz(zjS2v{(rHim-hew diff --git a/crates/view/src/service.rs b/crates/view/src/service.rs index 54188fce61..90f0b3c281 100644 --- a/crates/view/src/service.rs +++ b/crates/view/src/service.rs @@ -279,13 +279,13 @@ impl ViewService { #[instrument(skip(self))] pub async fn status(&self) -> anyhow::Result { - let sync_height = self.storage.last_sync_height().await?.unwrap_or(0); + let full_sync_height = self.storage.last_sync_height().await?.unwrap_or(0); let (latest_known_block_height, node_catching_up) = self.latest_known_block_height().await?; let height_diff = latest_known_block_height - .checked_sub(sync_height) + .checked_sub(full_sync_height) .ok_or_else(|| anyhow!("sync height ahead of node height"))?; let catching_up = match (node_catching_up, height_diff) { @@ -300,8 +300,9 @@ impl ViewService { }; Ok(StatusResponse { - sync_height, + full_sync_height, catching_up, + partial_sync_height: full_sync_height, // Set these as the same for backwards compatibility following adding the partial_sync_height }) } } @@ -1048,7 +1049,8 @@ impl ViewProtocolService for ViewService { while let Some(sync_height) = sync_height_stream.next().await { yield pb::StatusStreamResponse { latest_known_block_height, - sync_height, + full_sync_height: sync_height, + partial_sync_height: sync_height, // Set these as the same for backwards compatibility following adding the partial_sync_height }; if sync_height >= latest_known_block_height { break; diff --git a/crates/view/src/status.rs b/crates/view/src/status.rs index 8cbdcbbba7..3a9b465331 100644 --- a/crates/view/src/status.rs +++ b/crates/view/src/status.rs @@ -3,7 +3,8 @@ use penumbra_proto::{view::v1alpha1 as pb, DomainType, TypeUrl}; #[derive(Clone, Copy, Debug)] pub struct StatusStreamResponse { pub latest_known_block_height: u64, - pub sync_height: u64, + pub full_sync_height: u64, + pub partial_sync_height: u64, } impl TypeUrl for StatusStreamResponse { @@ -20,7 +21,8 @@ impl TryFrom for StatusStreamResponse { fn try_from(proto: pb::StatusStreamResponse) -> Result { Ok(StatusStreamResponse { latest_known_block_height: proto.latest_known_block_height, - sync_height: proto.sync_height, + full_sync_height: proto.full_sync_height, + partial_sync_height: proto.partial_sync_height, }) } } @@ -29,7 +31,8 @@ impl From for pb::StatusStreamResponse { fn from(msg: StatusStreamResponse) -> Self { pb::StatusStreamResponse { latest_known_block_height: msg.latest_known_block_height, - sync_height: msg.sync_height, + full_sync_height: msg.full_sync_height, + partial_sync_height: msg.partial_sync_height, } } } diff --git a/proto/penumbra/penumbra/view/v1alpha1/view.proto b/proto/penumbra/penumbra/view/v1alpha1/view.proto index 0ef4936987..d717e40371 100644 --- a/proto/penumbra/penumbra/view/v1alpha1/view.proto +++ b/proto/penumbra/penumbra/view/v1alpha1/view.proto @@ -287,10 +287,12 @@ message StatusRequest { // Returns the status of the view service and whether it is synchronized with the chain state. message StatusResponse { - // The height the view service has synchronized to so far - uint64 sync_height = 1; + // The height the view service has synchronized to so far when doing a full linear sync + uint64 full_sync_height = 1; + // The height the view service has synchronized to so far when doing a partial sync + uint64 partial_sync_height = 2; // Whether the view service is catching up with the chain state - bool catching_up = 2; + bool catching_up = 3; } // Requests streaming updates on the sync height until the view service is synchronized. @@ -301,8 +303,12 @@ message StatusStreamRequest { // A streaming sync status update message StatusStreamResponse { + // The latest known block height uint64 latest_known_block_height = 1; - uint64 sync_height = 2; + // The height the view service has synchronized to so far when doing a full linear sync + uint64 full_sync_height = 2; + // The height the view service has synchronized to so far when doing a partial sync + uint64 partial_sync_height = 3; } // A query for notes known by the view service.