1use std::path::Path;
16
17use anyhow::{anyhow, bail, Context, Result};
18use bytes::Bytes;
19use prost::Message;
20use risc0_zkp::core::digest::Digest;
21
22use super::{
23 malformed_err, pb, Asset, AssetRequest, ConnectionWrapper, Connector, ParentProcessConnector,
24 SessionInfo,
25};
26use crate::{
27 claim::receipt::UnionClaim,
28 get_version,
29 host::{api::SegmentInfo, client::prove::get_r0vm_path},
30 receipt::{AssumptionReceipt, SegmentReceipt, SuccinctReceipt},
31 ExecutorEnv, Journal, ProveInfo, ProverOpts, Receipt, ReceiptClaim, Unknown,
32};
33
34pub(crate) enum Compat {
35 Narrow,
36 Wide,
37}
38
39pub struct Client {
41 connector: Box<dyn Connector>,
42 compat: Compat,
43 version_override: Option<semver::Version>,
44}
45
46impl Default for Client {
47 fn default() -> Self {
48 Self::new().unwrap()
49 }
50}
51
52impl Client {
53 pub fn new() -> Result<Self> {
55 Self::new_sub_process("r0vm")
56 }
57
58 pub fn new_sub_process<P: AsRef<Path>>(server_path: P) -> Result<Self> {
61 let connector = ParentProcessConnector::new_narrow_version(server_path)?;
62 Ok(Self::with_connector(Box::new(connector)))
63 }
64
65 pub fn new_sub_process_compat<P: AsRef<Path>>(
70 server_path: P,
71 version_override: semver::Version,
72 ) -> Result<Self> {
73 let connector = ParentProcessConnector::new(server_path)?;
74 Ok(Self {
75 connector: Box::new(connector),
76 compat: Compat::Wide,
77 version_override: Some(version_override),
78 })
79 }
80
81 pub fn from_env() -> Result<Self> {
83 Client::new_sub_process(get_r0vm_path()?)
84 }
85
86 pub fn with_connector(connector: Box<dyn Connector>) -> Self {
89 Self {
90 connector,
91 compat: Compat::Narrow,
92 version_override: None,
93 }
94 }
95
96 pub fn prove(
98 &self,
99 env: &ExecutorEnv<'_>,
100 opts: &ProverOpts,
101 binary: Asset,
102 ) -> Result<ProveInfo> {
103 let mut conn = self.connect()?;
104
105 let request = pb::api::ServerRequest {
106 kind: Some(pb::api::server_request::Kind::Prove(
107 pb::api::ProveRequest {
108 env: Some(self.make_execute_env(env, binary.try_into()?)?),
109 opts: Some(opts.clone().into()),
110 receipt_out: Some(pb::api::AssetRequest {
111 kind: Some(pb::api::asset_request::Kind::Inline(())),
112 }),
113 },
114 )),
115 };
116 conn.send(request)?;
117
118 let asset = self.prove_handler(&mut conn, env)?;
119
120 let code = conn.close()?;
121 if code != 0 {
122 bail!("Child finished with: {code}");
123 }
124
125 let prove_info_bytes = asset.as_bytes()?;
126 let prove_info_pb = pb::core::ProveInfo::decode(prove_info_bytes)?;
127 prove_info_pb.try_into()
128 }
129
130 pub fn execute<F>(
132 &self,
133 env: &ExecutorEnv<'_>,
134 binary: Asset,
135 segments_out: AssetRequest,
136 segment_callback: F,
137 ) -> Result<SessionInfo>
138 where
139 F: FnMut(SegmentInfo, Asset) -> Result<()>,
140 {
141 let mut conn = self.connect()?;
142
143 let request = pb::api::ServerRequest {
144 kind: Some(pb::api::server_request::Kind::Execute(
145 pb::api::ExecuteRequest {
146 env: Some(self.make_execute_env(env, binary.try_into()?)?),
147 segments_out: Some(segments_out.try_into()?),
148 },
149 )),
150 };
151 conn.send(request)?;
153
154 let result = self.execute_handler(segment_callback, &mut conn, env);
155
156 let code = conn.close()?;
157 if code != 0 {
158 bail!("Child finished with: {code}");
159 }
160
161 result
162 }
163
164 pub fn prove_segment(
166 &self,
167 opts: &ProverOpts,
168 segment: Asset,
169 receipt_out: AssetRequest,
170 ) -> Result<SegmentReceipt> {
171 let mut conn = self.connect()?;
172
173 let request = pb::api::ServerRequest {
174 kind: Some(pb::api::server_request::Kind::ProveSegment(
175 pb::api::ProveSegmentRequest {
176 opts: Some(opts.clone().into()),
177 segment: Some(segment.try_into()?),
178 receipt_out: Some(receipt_out.try_into()?),
179 },
180 )),
181 };
182 conn.send(request).context("tx request failed")?;
184
185 let reply: pb::api::ProveSegmentReply = conn.recv().context("rx reply failed")?;
186
187 let result = match reply
188 .kind
189 .ok_or_else(|| malformed_err("ProveSegmentReply.kind"))?
190 {
191 pb::api::prove_segment_reply::Kind::Ok(result) => {
192 let receipt_bytes = result
193 .receipt
194 .ok_or_else(|| malformed_err("ProveSegmentReply.Ok.receipt"))?
195 .as_bytes()?;
196 let receipt_pb = pb::core::SegmentReceipt::decode(receipt_bytes)?;
197 receipt_pb.try_into()
198 }
199 pb::api::prove_segment_reply::Kind::Error(err) => Err(err.into()),
200 };
201
202 let code = conn.close()?;
203 if code != 0 {
204 bail!("Child finished with: {code}");
205 }
206
207 result
208 }
209
210 pub fn prove_keccak(
212 &self,
213 proof_request: crate::host::client::env::ProveKeccakRequest,
214 receipt_out: AssetRequest,
215 ) -> Result<SuccinctReceipt<Unknown>> {
216 use crate::host::api::convert::keccak_input_to_bytes;
217
218 let mut conn = self.connect()?;
219
220 let request = pb::api::ServerRequest {
221 kind: Some(pb::api::server_request::Kind::ProveKeccak(
222 pb::api::ProveKeccakRequest {
223 claim_digest: Some(proof_request.claim_digest.into()),
224 po2: proof_request.po2 as u32,
225 control_root: Some(proof_request.control_root.into()),
226 input: keccak_input_to_bytes(&proof_request.input),
227 receipt_out: Some(receipt_out.try_into()?),
228 },
229 )),
230 };
231
232 tracing::trace!("tx: {request:?}");
233 conn.send(request)?;
234
235 let reply: pb::api::ProveKeccakReply = conn.recv()?;
236
237 let result = match reply
238 .kind
239 .ok_or_else(|| malformed_err("ProveKeccakReply.kind"))?
240 {
241 pb::api::prove_keccak_reply::Kind::Ok(result) => {
242 let receipt_bytes = result
243 .receipt
244 .ok_or_else(|| malformed_err("ProveKeccakReply.Ok.receipt"))?
245 .as_bytes()?;
246 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
247 receipt_pb.try_into()
248 }
249 pb::api::prove_keccak_reply::Kind::Error(err) => Err(err.into()),
250 };
251
252 let code = conn.close()?;
253 if code != 0 {
254 bail!("Child finished with: {code}");
255 }
256
257 result
258 }
259
260 pub fn lift(
267 &self,
268 opts: &ProverOpts,
269 receipt: Asset,
270 receipt_out: AssetRequest,
271 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
272 let mut conn = self.connect()?;
273
274 let request = pb::api::ServerRequest {
275 kind: Some(pb::api::server_request::Kind::Lift(pb::api::LiftRequest {
276 opts: Some(opts.clone().into()),
277 receipt: Some(receipt.try_into()?),
278 receipt_out: Some(receipt_out.try_into()?),
279 })),
280 };
281 conn.send(request)?;
283
284 let reply: pb::api::LiftReply = conn.recv()?;
285
286 let result = match reply.kind.ok_or_else(|| malformed_err("LiftReply.kind"))? {
287 pb::api::lift_reply::Kind::Ok(result) => {
288 let receipt_bytes = result
289 .receipt
290 .ok_or_else(|| malformed_err("LiftReply.Ok.receipt"))?
291 .as_bytes()?;
292 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
293 receipt_pb.try_into()
294 }
295 pb::api::lift_reply::Kind::Error(err) => Err(err.into()),
296 };
297
298 let code = conn.close()?;
299 if code != 0 {
300 bail!("Child finished with: {code}");
301 }
302
303 result
304 }
305
306 pub fn join(
311 &self,
312 opts: &ProverOpts,
313 left_receipt: Asset,
314 right_receipt: Asset,
315 receipt_out: AssetRequest,
316 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
317 let mut conn = self.connect()?;
318
319 let request = pb::api::ServerRequest {
320 kind: Some(pb::api::server_request::Kind::Join(pb::api::JoinRequest {
321 opts: Some(opts.clone().into()),
322 left_receipt: Some(left_receipt.try_into()?),
323 right_receipt: Some(right_receipt.try_into()?),
324 receipt_out: Some(receipt_out.try_into()?),
325 })),
326 };
327 conn.send(request)?;
329
330 let reply: pb::api::JoinReply = conn.recv()?;
331
332 let result = match reply.kind.ok_or_else(|| malformed_err("JoinReply.kind"))? {
333 pb::api::join_reply::Kind::Ok(result) => {
334 let receipt_bytes = result
335 .receipt
336 .ok_or_else(|| malformed_err("JoinReply.Ok.receipt"))?
337 .as_bytes()?;
338 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
339 receipt_pb.try_into()
340 }
341 pb::api::join_reply::Kind::Error(err) => Err(err.into()),
342 };
343
344 let code = conn.close()?;
345 if code != 0 {
346 bail!("Child finished with: {code}");
347 }
348
349 result
350 }
351
352 pub fn union(
357 &self,
358 opts: &ProverOpts,
359 a_receipt: Asset,
360 b_receipt: Asset,
361 receipt_out: AssetRequest,
362 ) -> Result<SuccinctReceipt<UnionClaim>> {
363 let mut conn = self.connect()?;
364
365 let request = pb::api::ServerRequest {
366 kind: Some(pb::api::server_request::Kind::Union(
367 pb::api::UnionRequest {
368 opts: Some(opts.clone().into()),
369 left_receipt: Some(a_receipt.try_into()?),
370 right_receipt: Some(b_receipt.try_into()?),
371 receipt_out: Some(receipt_out.try_into()?),
372 },
373 )),
374 };
375 conn.send(request)?;
377
378 let reply: pb::api::UnionReply = conn.recv()?;
379
380 let result = match reply.kind.ok_or_else(|| malformed_err("UnionReply.kind"))? {
381 pb::api::union_reply::Kind::Ok(result) => {
382 let receipt_bytes = result
383 .receipt
384 .ok_or_else(|| malformed_err("UnionReply.Ok.receipt"))?
385 .as_bytes()?;
386 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
387 receipt_pb.try_into()
388 }
389 pb::api::union_reply::Kind::Error(err) => Err(err.into()),
390 };
391
392 let code = conn.close()?;
393 if code != 0 {
394 bail!("Child finished with: {code}");
395 }
396
397 result
398 }
399
400 pub fn resolve(
407 &self,
408 opts: &ProverOpts,
409 conditional_receipt: Asset,
410 assumption_receipt: Asset,
411 receipt_out: AssetRequest,
412 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
413 let mut conn = self.connect()?;
414
415 let request = pb::api::ServerRequest {
416 kind: Some(pb::api::server_request::Kind::Resolve(
417 pb::api::ResolveRequest {
418 opts: Some(opts.clone().into()),
419 conditional_receipt: Some(conditional_receipt.try_into()?),
420 assumption_receipt: Some(assumption_receipt.try_into()?),
421 receipt_out: Some(receipt_out.try_into()?),
422 },
423 )),
424 };
425 conn.send(request)?;
427
428 let reply: pb::api::ResolveReply = conn.recv()?;
429
430 let result = match reply
431 .kind
432 .ok_or_else(|| malformed_err("ResolveReply.kind"))?
433 {
434 pb::api::resolve_reply::Kind::Ok(result) => {
435 let receipt_bytes = result
436 .receipt
437 .ok_or_else(|| malformed_err("ResolveReply.Ok.receipt"))?
438 .as_bytes()?;
439 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
440 receipt_pb.try_into()
441 }
442 pb::api::resolve_reply::Kind::Error(err) => Err(err.into()),
443 };
444
445 let code = conn.close()?;
446 if code != 0 {
447 bail!("Child finished with: {code}");
448 }
449
450 result
451 }
452
453 pub fn identity_p254(
459 &self,
460 opts: &ProverOpts,
461 receipt: Asset,
462 receipt_out: AssetRequest,
463 ) -> Result<SuccinctReceipt<ReceiptClaim>> {
464 let mut conn = self.connect()?;
465
466 let request = pb::api::ServerRequest {
467 kind: Some(pb::api::server_request::Kind::IdentityP254(
468 pb::api::IdentityP254Request {
469 opts: Some(opts.clone().into()),
470 receipt: Some(receipt.try_into()?),
471 receipt_out: Some(receipt_out.try_into()?),
472 },
473 )),
474 };
475 conn.send(request)?;
477
478 let reply: pb::api::IdentityP254Reply = conn.recv()?;
479
480 let result = match reply
481 .kind
482 .ok_or_else(|| malformed_err("IdentityP254Reply.kind"))?
483 {
484 pb::api::identity_p254_reply::Kind::Ok(result) => {
485 let receipt_bytes = result
486 .receipt
487 .ok_or_else(|| malformed_err("IdentityP254Reply.Ok.receipt"))?
488 .as_bytes()?;
489 let receipt_pb = pb::core::SuccinctReceipt::decode(receipt_bytes)?;
490 receipt_pb.try_into()
491 }
492 pb::api::identity_p254_reply::Kind::Error(err) => Err(err.into()),
493 };
494
495 let code = conn.close()?;
496 if code != 0 {
497 bail!("Child finished with: {code}");
498 }
499
500 result
501 }
502
503 pub fn compress(
528 &self,
529 opts: &ProverOpts,
530 receipt: Asset,
531 receipt_out: AssetRequest,
532 ) -> Result<Receipt> {
533 let mut conn = self.connect()?;
534
535 let request = pb::api::ServerRequest {
536 kind: Some(pb::api::server_request::Kind::Compress(
537 pb::api::CompressRequest {
538 opts: Some(opts.clone().into()),
539 receipt: Some(receipt.try_into()?),
540 receipt_out: Some(receipt_out.try_into()?),
541 },
542 )),
543 };
544 conn.send(request)?;
546
547 let reply: pb::api::CompressReply = conn.recv()?;
548
549 let result = match reply
550 .kind
551 .ok_or_else(|| malformed_err("CompressReply.kind"))?
552 {
553 pb::api::compress_reply::Kind::Ok(result) => {
554 let receipt_bytes = result
555 .receipt
556 .ok_or_else(|| malformed_err("CompressReply.Ok.receipt"))?
557 .as_bytes()?;
558 let receipt_pb = pb::core::Receipt::decode(receipt_bytes)?;
559 receipt_pb.try_into()
560 }
561 pb::api::compress_reply::Kind::Error(err) => Err(err.into()),
562 };
563
564 let code = conn.close()?;
565 if code != 0 {
566 bail!("Child finished with: {code}");
567 }
568
569 result
570 }
571
572 pub fn verify(&self, receipt: Asset, image_id: impl Into<Digest>) -> Result<()> {
574 let mut conn = self.connect().context("connect")?;
575 let image_id = image_id.into();
576
577 let request = pb::api::ServerRequest {
578 kind: Some(pb::api::server_request::Kind::Verify(
579 pb::api::VerifyRequest {
580 receipt: Some(receipt.try_into().context("convert receipt asset")?),
581 image_id: Some(image_id.into()),
582 },
583 )),
584 };
585 conn.send(request).context("send")?;
587
588 let reply: pb::api::GenericReply = conn.recv().context("error from server")?;
589 let result = match reply
590 .kind
591 .ok_or_else(|| malformed_err("GenericReply.kind"))?
592 {
593 pb::api::generic_reply::Kind::Ok(ok) => Ok(ok),
594 pb::api::generic_reply::Kind::Error(err) => Err(err.into()),
595 };
596
597 let code = conn.close().context("close")?;
598 if code != 0 {
599 bail!("Child finished with: {code}");
600 }
601
602 result
603 }
604
605 fn connect(&self) -> Result<ConnectionWrapper> {
606 let mut conn = self.connector.connect()?;
607
608 let client_version = get_version().map_err(|err| anyhow!(err))?;
609 let client_version = self.version_override.as_ref().unwrap_or(&client_version);
610 let request = pb::api::HelloRequest {
611 version: Some(client_version.clone().into()),
612 };
613 conn.send(request)?;
615
616 let reply: pb::api::HelloReply = conn.recv()?;
617 match reply.kind.ok_or_else(|| malformed_err("HelloReply.kind"))? {
619 pb::api::hello_reply::Kind::Ok(reply) => {
620 let server_version: semver::Version = reply
621 .version
622 .ok_or_else(|| malformed_err("HelloReply.Ok.version"))?
623 .try_into()
624 .map_err(|err: semver::Error| anyhow!(err))?;
625
626 if !self.compat.check(client_version, &server_version) {
627 let msg = format!("incompatible server version: {server_version}");
628 tracing::warn!("{msg}");
629 bail!(msg);
630 }
631 }
632 pb::api::hello_reply::Kind::Error(err) => {
633 let code = conn.close()?;
634 tracing::debug!("Child finished with: {code}");
635 bail!(err);
636 }
637 }
638
639 Ok(conn)
640 }
641
642 fn make_execute_env(
643 &self,
644 env: &ExecutorEnv<'_>,
645 binary: pb::api::Asset,
646 ) -> Result<pb::api::ExecutorEnv> {
647 Ok(pb::api::ExecutorEnv {
648 binary: Some(binary),
649 env_vars: env.env_vars.clone(),
650 args: env.args.clone(),
651 slice_ios: env.slice_io.borrow().inner.keys().cloned().collect(),
652 read_fds: env.posix_io.borrow().read_fds(),
653 write_fds: env.posix_io.borrow().write_fds(),
654 segment_limit_po2: env.segment_limit_po2,
655 keccak_max_po2: env.keccak_max_po2,
656 session_limit: env.session_limit,
657 trace_events: (!env.trace.is_empty()).then_some(()),
658 coprocessor: env.coprocessor.is_some(),
659 pprof_out: env
660 .pprof_out
661 .as_ref()
662 .map(|x| x.to_string_lossy().into())
663 .unwrap_or_default(),
664 assumptions: env
665 .assumptions
666 .borrow()
667 .0
668 .iter()
669 .map(|a| {
670 Ok(match a {
671 AssumptionReceipt::Proven(inner) => pb::api::AssumptionReceipt {
672 kind: Some(pb::api::assumption_receipt::Kind::Proven(
673 Asset::Inline(
674 pb::core::InnerReceipt::try_from(inner.clone())?
675 .encode_to_vec()
676 .into(),
677 )
678 .try_into()?,
679 )),
680 },
681 AssumptionReceipt::Unresolved(assumption) => pb::api::AssumptionReceipt {
682 kind: Some(pb::api::assumption_receipt::Kind::Unresolved(
683 Asset::Inline(
684 pb::core::Assumption::try_from(assumption.clone())?
685 .encode_to_vec()
686 .into(),
687 )
688 .try_into()?,
689 )),
690 },
691 })
692 })
693 .collect::<Result<_>>()?,
694 segment_path: env
695 .segment_path
696 .as_ref()
697 .map(|x| x.path().to_string_lossy().into())
698 .unwrap_or_default(),
699 povw_job_id: env
700 .povw_job_id
701 .map(|x| x.to_bytes().to_vec())
702 .unwrap_or_default(),
703 })
704 }
705
706 fn execute_handler<F>(
707 &self,
708 segment_callback: F,
709 conn: &mut ConnectionWrapper,
710 env: &ExecutorEnv<'_>,
711 ) -> Result<SessionInfo>
712 where
713 F: FnMut(SegmentInfo, Asset) -> Result<()>,
714 {
715 let mut segment_callback = segment_callback;
716 let mut segments = Vec::new();
717 loop {
718 let reply: pb::api::ServerReply = conn.recv()?;
719 match reply
722 .kind
723 .ok_or_else(|| malformed_err("ServerReply.kind"))?
724 {
725 pb::api::server_reply::Kind::Ok(request) => {
726 match request
727 .kind
728 .ok_or_else(|| malformed_err("ServerReply.Ok.kind"))?
729 {
730 pb::api::client_callback::Kind::Io(io) => {
731 let msg: pb::api::OnIoReply = self.on_io(env, io).into();
732 conn.send(msg)?;
734 }
735 pb::api::client_callback::Kind::SegmentDone(segment) => {
736 let reply: pb::api::GenericReply = segment
737 .segment
738 .map_or_else(
739 || Err(malformed_err("ServerReply.Ok.SegmentDone.segment")),
740 |segment| {
741 let asset = segment
742 .segment
743 .ok_or_else(|| {
744 malformed_err(
745 "ServerReply.Ok.SegmentDone.segment.segment",
746 )
747 })?
748 .try_into()?;
749 let info = SegmentInfo {
750 po2: segment.po2,
751 cycles: segment.cycles,
752 };
753 segments.push(info.clone());
754 segment_callback(info, asset)
755 },
756 )
757 .into();
758 conn.send(reply)?;
760 }
761 pb::api::client_callback::Kind::SessionDone(session) => {
762 return match session.session {
763 Some(session) => {
764 let receipt_claim = match session.receipt_claim {
765 Some(claim) => Some(
766 pb::core::ReceiptClaim::decode(claim.as_bytes()?)?
767 .try_into()?,
768 ),
769 None => None,
770 };
771
772 Ok(SessionInfo {
773 segments,
774 journal: Journal::new(session.journal),
775 exit_code: session
776 .exit_code
777 .ok_or_else(|| malformed_err("SessionInfo.exit_code"))?
778 .try_into()?,
779 receipt_claim,
780 })
781 }
782 None => Err(malformed_err("ServerReply.ok.SessionDone.session")),
783 }
784 }
785 pb::api::client_callback::Kind::ProveDone(_) => {
786 return Err(anyhow!("Illegal client callback"))
787 }
788 }
789 }
790 pb::api::server_reply::Kind::Error(err) => return Err(err.into()),
791 }
792 }
793 }
794
795 fn prove_handler(
796 &self,
797 conn: &mut ConnectionWrapper,
798 env: &ExecutorEnv<'_>,
799 ) -> Result<pb::api::Asset> {
800 loop {
801 let reply: pb::api::ServerReply = conn.recv()?;
802 match reply
805 .kind
806 .ok_or_else(|| malformed_err("ServerReply.kind"))?
807 {
808 pb::api::server_reply::Kind::Ok(request) => {
809 match request
810 .kind
811 .ok_or_else(|| malformed_err("ServerReply.Ok.kind"))?
812 {
813 pb::api::client_callback::Kind::Io(io) => {
814 let msg: pb::api::OnIoReply = self.on_io(env, io).into();
815 conn.send(msg)?;
817 }
818 pb::api::client_callback::Kind::SegmentDone(_) => {
819 return Err(anyhow!("Illegal client callback"))
820 }
821 pb::api::client_callback::Kind::SessionDone(_) => {
822 return Err(anyhow!("Illegal client callback"))
823 }
824 pb::api::client_callback::Kind::ProveDone(done) => {
825 return done.prove_info.ok_or_else(|| {
826 malformed_err("ServerReply.Ok.ProveDone.prove_info")
827 })
828 }
829 }
830 }
831 pb::api::server_reply::Kind::Error(err) => return Err(err.into()),
832 }
833 }
834 }
835
836 fn on_io(&self, env: &ExecutorEnv<'_>, request: pb::api::OnIoRequest) -> Result<Bytes> {
837 match request
838 .kind
839 .ok_or_else(|| malformed_err("OnIoRequest.kind"))?
840 {
841 pb::api::on_io_request::Kind::Posix(posix) => {
842 let cmd = posix
843 .cmd
844 .ok_or_else(|| malformed_err("OnIoRequest.Posix.cmd"))?;
845 match cmd
846 .kind
847 .ok_or_else(|| malformed_err("OnIoRequest.Posix.cmd.kind"))?
848 {
849 pb::api::posix_cmd::Kind::Read(nread) => {
850 self.on_posix_read(env, posix.fd, nread as usize)
851 }
852 pb::api::posix_cmd::Kind::Write(from_guest) => {
853 self.on_posix_write(env, posix.fd, from_guest.into())?;
854 Ok(Bytes::new())
855 }
856 }
857 }
858 pb::api::on_io_request::Kind::Slice(slice_io) => {
859 self.on_slice(env, &slice_io.name, slice_io.from_guest.into())
860 }
861 pb::api::on_io_request::Kind::Trace(event) => {
862 self.on_trace(env, event)?;
863 Ok(Bytes::new())
864 }
865 pb::api::on_io_request::Kind::Coprocessor(request) => {
866 self.on_coprocessor(env, request)?;
867 Ok(Bytes::new())
868 }
869 }
870 }
871
872 fn on_posix_read(&self, env: &ExecutorEnv<'_>, fd: u32, nread: usize) -> Result<Bytes> {
873 tracing::debug!("on_posix_read: {fd}, {nread}");
874 let mut from_host = vec![0; nread];
875 let posix_io = env.posix_io.borrow();
876 let reader = posix_io.get_reader(fd)?;
877 let nread = reader.borrow_mut().read(&mut from_host)?;
878 let slice = from_host[..nread].to_vec();
879 Ok(slice.into())
880 }
881
882 fn on_posix_write(&self, env: &ExecutorEnv<'_>, fd: u32, from_guest: Bytes) -> Result<()> {
883 tracing::debug!("on_posix_write: {fd}");
884 let posix_io = env.posix_io.borrow();
885 let writer = posix_io.get_writer(fd)?;
886 writer.borrow_mut().write_all(&from_guest)?;
887 Ok(())
888 }
889
890 fn on_slice(&self, env: &ExecutorEnv<'_>, name: &str, from_guest: Bytes) -> Result<Bytes> {
891 let table = env.slice_io.borrow();
892 let slice_io = table
893 .inner
894 .get(name)
895 .ok_or_else(|| anyhow!("Unknown I/O channel name: {name}"))?;
896 let result = slice_io.borrow_mut().handle_io(name, from_guest)?;
897 Ok(result)
898 }
899
900 fn on_trace(&self, env: &ExecutorEnv<'_>, event: pb::api::TraceEvent) -> Result<()> {
901 for trace_callback in env.trace.iter() {
902 trace_callback
903 .borrow_mut()
904 .trace_callback(event.clone().try_into()?)?;
905 }
906 Ok(())
907 }
908
909 fn on_coprocessor(
910 &self,
911 env: &ExecutorEnv<'_>,
912 coprocessor_request: pb::api::CoprocessorRequest,
913 ) -> Result<()> {
914 match coprocessor_request
915 .kind
916 .ok_or_else(|| malformed_err("OnCoprocessorRequest.kind"))?
917 {
918 pb::api::coprocessor_request::Kind::ProveKeccak(proof_request) => {
919 let proof_request = proof_request.try_into()?;
920 let coprocessor = env
921 .coprocessor
922 .clone()
923 .ok_or_else(|| malformed_err("OnCoprocessorRequest.ProveKeccak.coprocessor"))?;
924 let mut coprocessor = coprocessor.borrow_mut();
925 coprocessor.prove_keccak(proof_request)
926 }
927 }
928 }
929}
930
931impl From<Result<Bytes, anyhow::Error>> for pb::api::OnIoReply {
932 fn from(result: Result<Bytes, anyhow::Error>) -> Self {
933 Self {
934 kind: Some(match result {
935 Ok(bytes) => pb::api::on_io_reply::Kind::Ok(bytes.into()),
936 Err(err) => pb::api::on_io_reply::Kind::Error(err.into()),
937 }),
938 }
939 }
940}
941
942impl Compat {
943 pub(crate) fn check(&self, requested: &semver::Version, server: &semver::Version) -> bool {
944 match self {
945 Compat::Narrow => {
946 if requested.pre.is_empty() {
947 requested.major == server.major && requested.minor == server.minor
948 } else {
949 requested == server
950 }
951 }
952 Compat::Wide => requested.major == server.major,
953 }
954 }
955}
956
957#[cfg(test)]
958mod tests {
959 use semver::Version;
960
961 use super::Compat;
962
963 #[test]
964 fn check_version_narrow() {
965 fn test(requested: &str, server: &str) -> bool {
966 Compat::Narrow.check(
967 &Version::parse(requested).unwrap(),
968 &Version::parse(server).unwrap(),
969 )
970 }
971
972 assert!(test("0.18.0", "0.18.0"));
973 assert!(test("0.18.0", "0.18.1"));
974 assert!(test("0.18.1", "0.18.1"));
975 assert!(test("0.18.1", "0.18.2"));
976 assert!(test("0.18.1", "0.18.0"));
977 assert!(!test("0.18.0", "0.19.0"));
978
979 assert!(test("1.0.0", "1.0.0"));
980 assert!(test("1.0.0", "1.0.1"));
981 assert!(test("1.1.0", "1.1.0"));
982 assert!(test("1.1.0", "1.1.1"));
983 assert!(!test("1.0.0", "0.18.0"));
984 assert!(!test("1.0.0", "2.0.0"));
985 assert!(!test("1.1.0", "1.0.0"));
986 assert!(test("1.0.3", "1.0.1"));
987
988 assert!(test("0.19.0-alpha.1", "0.19.0-alpha.1"));
989 assert!(!test("0.19.0-alpha.1", "0.19.0-alpha.2"));
990 }
991
992 #[test]
993 fn check_wide_version() {
994 fn test(requested: &str, server: &str) -> bool {
995 Compat::Wide.check(
996 &Version::parse(requested).unwrap(),
997 &Version::parse(server).unwrap(),
998 )
999 }
1000
1001 assert!(test("0.1.0", "0.1.0"));
1002 assert!(test("0.1.0", "0.1.1"));
1003 assert!(test("0.1.0", "0.2.0"));
1004 assert!(test("0.1.0-rc.1", "0.2.0"));
1005 assert!(test("1.1.0", "1.0.0"));
1006 assert!(!test("1.0.0", "2.0.0"));
1007 }
1008}