#![warn(missing_docs)]
use std::sync::Arc;
use bytes::Bytes;
use rings_core::dht::Did;
use serde::Deserialize;
use serde::Serialize;
use super::SNARKBehaviour;
use super::SNARKTaskManager;
use super::TaskId;
use super::NAMESPACE;
use crate::error::Error;
use crate::extension::ext::Ctx;
use crate::extension::ext::Interpret;
use crate::extension::ext::Protocol;
use crate::extension::ext::Reject;
use crate::extension::ext::Scope;
use crate::extension::ext::Transition;
use crate::extension::ext::Wire;
use crate::extension::types::snark::SNARKProofTask;
use crate::extension::types::snark::SNARKTask;
use crate::extension::types::snark::SNARKTaskMessage;
use crate::extension::types::snark::SNARKVerifyTask;
#[derive(Serialize, Deserialize)]
pub enum ComputeResult {
Proved {
task_id: TaskId,
reply_to: Did,
verify_task: SNARKVerifyTask,
},
Verified {
task_id: TaskId,
},
}
pub enum SnarkEvent {
Result(ComputeResult),
Task {
from: Did,
msg: SNARKTaskMessage,
},
}
pub enum SnarkEffect {
SendTask {
to: Did,
msg: SNARKTaskMessage,
},
Prove {
task_id: TaskId,
reply_to: Did,
task: Box<SNARKProofTask>,
},
Verify {
task_id: TaskId,
verify_task: SNARKVerifyTask,
},
}
#[derive(Clone)]
pub struct SnarkProtocol;
impl Protocol for SnarkProtocol {
type State = ();
type Event = SnarkEvent;
type Effect = SnarkEffect;
fn namespace(&self) -> &str {
NAMESPACE
}
fn init(&self) {}
fn decode(&self, wire: Wire<'_>) -> Result<SnarkEvent, Reject> {
if wire.from == wire.me {
let result = bincode::deserialize::<ComputeResult>(wire.payload)
.map_err(|e| Reject(format!("bad snark result: {e}")))?;
Ok(SnarkEvent::Result(result))
} else {
let msg = bincode::deserialize::<SNARKTaskMessage>(wire.payload)
.map_err(|e| Reject(format!("bad snark task: {e}")))?;
Ok(SnarkEvent::Task {
from: wire.from,
msg,
})
}
}
fn step(&self, _ctx: Ctx<'_, ()>, event: SnarkEvent) -> Transition<(), SnarkEffect> {
match event {
SnarkEvent::Result(ComputeResult::Proved {
task_id,
reply_to,
verify_task,
}) => Transition::with((), vec![SnarkEffect::SendTask {
to: reply_to,
msg: SNARKTaskMessage {
task_id,
task: SNARKTask::SNARKVerify(verify_task),
},
}]),
SnarkEvent::Result(ComputeResult::Verified { .. }) => Transition::pure(()),
SnarkEvent::Task { from, msg } => match msg.task {
SNARKTask::SNARKProof(task) => Transition::with((), vec![SnarkEffect::Prove {
task_id: msg.task_id,
reply_to: from,
task,
}]),
SNARKTask::SNARKVerify(verify_task) => {
Transition::with((), vec![SnarkEffect::Verify {
task_id: msg.task_id,
verify_task,
}])
}
},
}
}
}
pub struct SnarkShell {
manager: Arc<SNARKTaskManager>,
}
impl SnarkShell {
pub fn new(manager: Arc<SNARKTaskManager>) -> Self {
Self { manager }
}
fn reinject(&self, result: &ComputeResult) -> crate::error::Result<Vec<Bytes>> {
let payload = bincode::serialize(result).map_err(|_| Error::EncodeError)?;
Ok(vec![Bytes::from(payload)])
}
}
#[cfg_attr(feature = "browser", async_trait::async_trait(?Send))]
#[cfg_attr(not(feature = "browser"), async_trait::async_trait)]
impl Interpret for SnarkShell {
type Effect = SnarkEffect;
async fn run(&self, scope: &Scope, effect: SnarkEffect) -> crate::error::Result<Vec<Bytes>> {
match effect {
SnarkEffect::SendTask { to, msg } => {
let payload = bincode::serialize(&msg).map_err(|_| Error::EncodeError)?;
scope.send(to, Bytes::from(payload)).await?;
Ok(Vec::new())
}
SnarkEffect::Prove {
task_id,
reply_to,
task,
} => {
let verify_task = SNARKBehaviour::handle_snark_proof_task(task.as_ref())?;
self.reinject(&ComputeResult::Proved {
task_id,
reply_to,
verify_task,
})
}
SnarkEffect::Verify {
task_id,
verify_task,
} => {
let Some(task) = self.manager.task.get(&task_id) else {
return Err(Error::ExtensionError(format!(
"no pending SNARK proof task for {task_id}; cannot verify"
)));
};
let verified =
SNARKBehaviour::handle_snark_verify_task(&verify_task, task.value())?;
drop(task);
self.manager.verified.insert(task_id, verified);
self.reinject(&ComputeResult::Verified { task_id })
}
}
}
}