tlsn_wasm/prover/
mod.rs

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    /// Set up the prover.
55    ///
56    /// This performs all MPC setup prior to establishing the connection to the
57    /// application server.
58    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    /// Send the HTTP request to the server.
75    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    /// Returns the transcript.
105    pub fn transcript(&self) -> Result<Transcript> {
106        let prover = self.state.try_as_closed()?;
107
108        Ok(Transcript::from(prover.transcript()))
109    }
110
111    /// Runs the notarization protocol.
112    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    /// Reveals data to the verifier and finalizes the protocol.
145    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    // TODO: return the body
185    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}