1mod config;
2
3pub use config::ProverConfig;
4
5use enum_try_as_inner::EnumTryAsInner;
6use futures::TryFutureExt;
7use http_body_util::{BodyExt, Full};
8use hyper::body::Bytes;
9use tls_client_async::TlsConnection;
10use tlsn_core::{
11 request::RequestConfig,
12 transcript::{Idx, TranscriptCommitConfigBuilder},
13};
14use tlsn_prover::{state, Prover};
15use tracing::info;
16use wasm_bindgen::{prelude::*, JsError};
17use wasm_bindgen_futures::spawn_local;
18use ws_stream_wasm::WsMeta;
19
20use crate::{io::FuturesIo, types::*};
21
22type Result<T> = std::result::Result<T, JsError>;
23
24#[wasm_bindgen(js_name = Prover)]
25pub struct JsProver {
26 state: State,
27}
28
29#[derive(Debug, EnumTryAsInner)]
30#[derive_err(Debug)]
31enum State {
32 Initialized(Prover<state::Initialized>),
33 Setup(Prover<state::Setup>),
34 Closed(Prover<state::Closed>),
35 Complete,
36 Error,
37}
38
39impl State {
40 fn take(&mut self) -> Self {
41 std::mem::replace(self, State::Error)
42 }
43}
44
45#[wasm_bindgen(js_class = Prover)]
46impl JsProver {
47 #[wasm_bindgen(constructor)]
48 pub fn new(config: ProverConfig) -> JsProver {
49 JsProver {
50 state: State::Initialized(Prover::new(config.into())),
51 }
52 }
53
54 pub async fn setup(&mut self, verifier_url: &str) -> Result<()> {
59 let prover = self.state.take().try_into_initialized()?;
60
61 info!("connecting to verifier");
62
63 let (_, verifier_conn) = WsMeta::connect(verifier_url, None).await?;
64
65 info!("connected to verifier");
66
67 let prover = prover.setup(verifier_conn.into_io()).await?;
68
69 self.state = State::Setup(prover);
70
71 Ok(())
72 }
73
74 pub async fn send_request(
76 &mut self,
77 ws_proxy_url: &str,
78 request: HttpRequest,
79 ) -> Result<HttpResponse> {
80 let prover = self.state.take().try_into_setup()?;
81
82 info!("connecting to server");
83
84 let (_, server_conn) = WsMeta::connect(ws_proxy_url, None).await?;
85
86 info!("connected to server");
87
88 let (tls_conn, prover_fut) = prover.connect(server_conn.into_io()).await?;
89
90 info!("sending request");
91
92 let (response, prover) = futures::try_join!(
93 send_request(tls_conn, request),
94 prover_fut.map_err(Into::into)
95 )?;
96
97 info!("response received");
98
99 self.state = State::Closed(prover);
100
101 Ok(response)
102 }
103
104 pub fn transcript(&self) -> Result<Transcript> {
106 let prover = self.state.try_as_closed()?;
107
108 Ok(Transcript::from(prover.transcript()))
109 }
110
111 pub async fn notarize(&mut self, commit: Commit) -> Result<NotarizationOutput> {
113 let mut prover = self.state.take().try_into_closed()?.start_notarize();
114
115 info!("starting notarization");
116
117 let mut builder = TranscriptCommitConfigBuilder::new(prover.transcript());
118
119 for range in commit.sent {
120 builder.commit_sent(&range)?;
121 }
122
123 for range in commit.recv {
124 builder.commit_recv(&range)?;
125 }
126
127 let config = builder.build()?;
128
129 prover.transcript_commit(config);
130
131 let request_config = RequestConfig::default();
132 let (attestation, secrets) = prover.finalize(&request_config).await?;
133
134 info!("notarization complete");
135
136 self.state = State::Complete;
137
138 Ok(NotarizationOutput {
139 attestation: attestation.into(),
140 secrets: secrets.into(),
141 })
142 }
143
144 pub async fn reveal(&mut self, reveal: Reveal) -> Result<()> {
146 let mut prover = self.state.take().try_into_closed()?.start_prove();
147
148 info!("revealing data");
149
150 let sent = Idx::new(reveal.sent);
151 let recv = Idx::new(reveal.recv);
152
153 prover.prove_transcript(sent, recv).await?;
154 prover.finalize().await?;
155
156 info!("Finalized");
157
158 self.state = State::Complete;
159
160 Ok(())
161 }
162}
163
164impl From<Prover<state::Initialized>> for JsProver {
165 fn from(value: Prover<state::Initialized>) -> Self {
166 JsProver {
167 state: State::Initialized(value),
168 }
169 }
170}
171
172async fn send_request(conn: TlsConnection, request: HttpRequest) -> Result<HttpResponse> {
173 let conn = FuturesIo::new(conn);
174 let request = hyper::Request::<Full<Bytes>>::try_from(request)?;
175
176 let (mut request_sender, conn) = hyper::client::conn::http1::handshake(conn).await?;
177
178 spawn_local(async move { conn.await.expect("connection runs to completion") });
179
180 let response = request_sender.send_request(request).await?;
181
182 let (response, body) = response.into_parts();
183
184 let _body = body.collect().await?;
186
187 let headers = response
188 .headers
189 .into_iter()
190 .map(|(k, v)| {
191 (
192 k.map(|k| k.to_string()).unwrap_or_default(),
193 v.as_bytes().to_vec(),
194 )
195 })
196 .collect();
197
198 Ok(HttpResponse {
199 status: response.status.as_u16(),
200 headers,
201 })
202}