diff --git a/src/lib.rs b/src/lib.rs index 5540876..0e6fffa 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ #![deny(unsafe_code)] #![allow(refining_impl_trait)] #![allow(dead_code)] - #![feature(async_fn_traits)] mod algebra; @@ -11,6 +10,6 @@ mod protocols; pub mod schemes; mod help; +pub mod marker; #[cfg(test)] mod testing; -pub mod marker; diff --git a/src/marker/exptree.rs b/src/marker/exptree.rs index 2c24084..047c52a 100644 --- a/src/marker/exptree.rs +++ b/src/marker/exptree.rs @@ -1,5 +1,12 @@ -use std::{collections::BTreeMap, ops::AsyncFn, sync::{atomic::{AtomicU32, Ordering::SeqCst}, Mutex}}; use crate::net::Communicate; +use std::{ + collections::BTreeMap, + ops::AsyncFn, + sync::{ + atomic::{AtomicU32, Ordering::SeqCst}, + Mutex, + }, +}; #[derive(Clone)] pub enum Status { Verified, @@ -23,7 +30,10 @@ impl ExpTree { pub fn add_dependent(&self, a: u32, b: u32) -> u32 { let new = self.issue(); - let status : Status = Status::Unverified { parents: (a, b), data: None }; + let status: Status = Status::Unverified { + parents: (a, b), + data: None, + }; { let mut tree = self.tree.lock().unwrap(); tree.insert(new, status); @@ -34,7 +44,10 @@ impl ExpTree { pub fn add_dependent_with(&self, a: u32, b: u32, data: T) -> u32 { let new = self.issue(); let data = Some(data); - let status = Status::Unverified { parents: (a, b), data }; + let status = Status::Unverified { + parents: (a, b), + data, + }; { let mut tree = self.tree.lock().unwrap(); tree.insert(new, status); @@ -53,7 +66,9 @@ impl ExpTree { } pub async fn verify(&mut self, id: u32, verify_fn: F, coms: C) -> Option - where F: AsyncFn(Vec, C) -> Option, C: Communicate + where + F: AsyncFn(Vec, C) -> Option, + C: Communicate, { let mut to_check = vec![]; let mut datas: Vec = vec![]; @@ -63,11 +78,11 @@ impl ExpTree { let tree = self.tree.get_mut().unwrap(); while let Some(id) = checking.pop() { - let t = tree.get(&id)?; + let t = tree.get(&id)?; match t { Status::Failure => todo!("Taint dependent values -or- crash and burn"), Status::Verified => (), - Status::Unverified{parents, data} => { + Status::Unverified { parents, data } => { checking.push(parents.0); checking.push(parents.1); if let Some(data) = data { @@ -79,14 +94,16 @@ impl ExpTree { } let res = verify_fn.async_call((datas, coms)).await?; - let status = if res { Status::Verified } else { Status::Failure }; + let status = if res { + Status::Verified + } else { + Status::Failure + }; for id in to_check { tree.insert(id, status.clone()); } Some(res) - } - } diff --git a/src/marker/mod.rs b/src/marker/mod.rs index 8ceef16..ba7bea3 100644 --- a/src/marker/mod.rs +++ b/src/marker/mod.rs @@ -3,8 +3,10 @@ mod exptree; use rand::RngCore; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use crate::{net::Communicate, schemes::{interactive::InteractiveShared, Shared, Verify}}; - +use crate::{ + net::Communicate, + schemes::{interactive::InteractiveShared, Shared, Verify}, +}; #[repr(transparent)] #[derive(Serialize, Debug, Clone)] @@ -23,7 +25,6 @@ impl<'de, S: Shared + DeserializeOwned> Deserialize<'de> for Unverified { } } - impl Unverified { pub async fn verify(self, coms: impl Communicate, args: S::Args) -> Option> { if self.0.verify(coms, args).await { @@ -40,26 +41,42 @@ impl Unverified { } } - impl Verified { - pub async fn open(self, ctx: &S::Context, coms: impl Communicate) -> Result { + pub async fn open( + self, + ctx: &S::Context, + coms: impl Communicate, + ) -> Result { S::recombine(ctx, self.0, coms).await } - pub async fn share(val: S::Value, ctx: &S::Context, rng: impl RngCore + Send, coms: impl Communicate) -> Result { + pub async fn share( + val: S::Value, + ctx: &S::Context, + rng: impl RngCore + Send, + coms: impl Communicate, + ) -> Result { let s = S::share(ctx, val, rng, coms).await?; Ok(Self(s)) } - } impl Unverified { - pub async fn share_symmetric(val: S::Value, ctx: &S::Context, rng: impl RngCore + Send, coms: impl Communicate) -> Result, S::Error> { + pub async fn share_symmetric( + val: S::Value, + ctx: &S::Context, + rng: impl RngCore + Send, + coms: impl Communicate, + ) -> Result, S::Error> { let s = S::symmetric_share(ctx, val, rng, coms).await?; Ok(s.into_iter().map(Self).collect()) } - pub async fn receive_share(ctx: &S::Context, coms: impl Communicate, from: usize) -> Result { + pub async fn receive_share( + ctx: &S::Context, + coms: impl Communicate, + from: usize, + ) -> Result { let s = S::receive_share(ctx, coms, from).await?; Ok(Self(s)) } @@ -77,13 +94,18 @@ impl From>> for Vec> { } } - impl Unverified> { - pub async fn verify_all(self, coms: impl Communicate, args: S::Args) -> Verified>> { + pub async fn verify_all( + self, + coms: impl Communicate, + args: S::Args, + ) -> Verified>> { let res = S::verify_many(&self.0, coms, args).await; - let res = res.into_iter().zip(self.0).map(|(verified, t)| { - verified.then_some(t) - }).collect(); + let res = res + .into_iter() + .zip(self.0) + .map(|(verified, t)| verified.then_some(t)) + .collect(); Verified(res) } } @@ -91,8 +113,8 @@ impl Unverified> { // Pure boring manual operator implementations // Could be done with some macros instead. mod ops { - use std::ops::{Add, Mul, Sub}; use crate::schemes::Shared; + use std::ops::{Add, Mul, Sub}; use super::*; @@ -144,7 +166,10 @@ mod ops { } } - impl Mul for Verified where S: Mul { + impl Mul for Verified + where + S: Mul, + { type Output = Self; fn mul(self, rhs: S::Value) -> Self::Output { @@ -152,7 +177,10 @@ mod ops { } } - impl Mul for Unverified where S: Mul { + impl Mul for Unverified + where + S: Mul, + { type Output = Self; fn mul(self, rhs: S::Value) -> Self::Output { @@ -179,7 +207,7 @@ mod test { me: 0, }; let mut rng = rngs::mock::StepRng::new(0, 0); - let s = as Shared>::share(&ctx, Mod11(3), &mut rng); + let s = as Shared>::share(&ctx, Mod11(3), &mut rng); let s = Verified(s[0]); let s0 = s.clone(); let s = s0 + s; diff --git a/src/net/agency.rs b/src/net/agency.rs index 37bc61c..096aad9 100644 --- a/src/net/agency.rs +++ b/src/net/agency.rs @@ -82,8 +82,9 @@ impl<'a, B: Broadcast> Broadcast for &'a mut B { msg: T, ) -> impl Future, Self::BroadcastError>> where - T: serde::Serialize + serde::de::DeserializeOwned + Sync { - (**self).symmetric_broadcast(msg) + T: serde::Serialize + serde::de::DeserializeOwned + Sync, + { + (**self).symmetric_broadcast(msg) } fn recv_from( @@ -166,7 +167,8 @@ impl<'a, U: Unicast> Unicast for &'a mut U { msgs: Vec, ) -> impl Future, Self::UnicastError>> where - T: serde::Serialize + serde::de::DeserializeOwned + Sync { + T: serde::Serialize + serde::de::DeserializeOwned + Sync, + { (**self).symmetric_unicast(msgs) } } diff --git a/src/net/connection.rs b/src/net/connection.rs index 5d5cc62..0212033 100644 --- a/src/net/connection.rs +++ b/src/net/connection.rs @@ -78,16 +78,16 @@ impl Connection { } } - pub struct Sending(FramedWrite); pub struct Receiving(FramedRead); - impl SendBytes for Sending { type SendError = ConnectionError; async fn send_bytes(&mut self, bytes: Bytes) -> Result<(), Self::SendError> { - SinkExt::<_>::send(&mut self.0, bytes).await.map_err(|_| ConnectionError::Closed) + SinkExt::<_>::send(&mut self.0, bytes) + .await + .map_err(|_| ConnectionError::Closed) } } @@ -95,7 +95,8 @@ impl RecvBytes for Receiving { type RecvError = ConnectionError; async fn recv_bytes(&mut self) -> Result { - self.0.next() + self.0 + .next() .await .ok_or(ConnectionError::Closed)? .map_err(|e| ConnectionError::Unknown(Box::new(e))) @@ -117,7 +118,6 @@ impl Connection pub async fn recv(&mut self) -> Result { self.receiver.recv().await } - } impl< @@ -148,7 +148,7 @@ impl< } } -impl Channel for Connection { +impl Channel for Connection { type Error = ConnectionError; } diff --git a/src/net/mod.rs b/src/net/mod.rs index 19b2035..32e1c68 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -8,7 +8,6 @@ pub mod connection; pub mod mux; pub mod network; - pub trait SendBytes: Send { type SendError: Error + Send + Sync + 'static; @@ -39,7 +38,6 @@ impl SendBytes for &mut S { } } - pub trait RecvBytes: Send { type RecvError: Error + Send + Sync + 'static; fn recv_bytes( @@ -64,7 +62,6 @@ impl RecvBytes for &mut R { ) -> impl std::future::Future> + Send { (**self).recv_bytes() } - } /// A communication medium between you and another party. @@ -77,17 +74,14 @@ impl Channel for &mut C { type Error = C::Error; } - - /// A [Channel] which can be split into a sender and receiver. pub trait SplitChannel: Channel + Send { type Sender: SendBytes + Send; type Receiver: RecvBytes + Send; fn split(&mut self) -> (&mut Self::Sender, &mut Self::Receiver); - } -impl<'a, C: SplitChannel> SplitChannel for &'a mut C { +impl<'a, C: SplitChannel> SplitChannel for &'a mut C { type Sender = C::Sender; type Receiver = C::Receiver; @@ -96,7 +90,6 @@ impl<'a, C: SplitChannel> SplitChannel for &'a mut C { } } - /// Tune to a specific channel pub trait Tuneable { type TuningError: Error + Send + 'static; @@ -138,8 +131,6 @@ impl<'a, R: Tuneable + ?Sized> Tuneable for &'a mut R { } } - /// General communication with support for most network functionality. -pub trait Communicate : agency::Broadcast + agency::Unicast + Tuneable + Send {} -impl<'a, C: Communicate> Communicate for &'a mut C { } - +pub trait Communicate: agency::Broadcast + agency::Unicast + Tuneable + Send {} +impl<'a, C: Communicate> Communicate for &'a mut C {} diff --git a/src/net/mux.rs b/src/net/mux.rs index 04bea50..c32846e 100644 --- a/src/net/mux.rs +++ b/src/net/mux.rs @@ -19,9 +19,7 @@ use tokio::join; use crate::{ help, - net::{ - network::Network, Channel, RecvBytes, SendBytes, SplitChannel - }, + net::{network::Network, Channel, RecvBytes, SendBytes, SplitChannel}, }; #[derive(Debug, Error)] diff --git a/src/net/network.rs b/src/net/network.rs index 68fc83f..049608b 100644 --- a/src/net/network.rs +++ b/src/net/network.rs @@ -44,9 +44,9 @@ pub struct Network { #[derive(thiserror::Error, Debug)] #[error("Error communicating with {id}: {source}")] -pub enum NetworkError { - Incoming {id: u32, source: E}, - Outgoing {id: u32, source: U} +pub enum NetworkError { + Incoming { id: u32, source: E }, + Outgoing { id: u32, source: U }, } #[allow(type_alias_bounds)] // It clearly matters, stop complaining @@ -69,22 +69,18 @@ impl Network { } } - /// Broadcast a message to all other parties. /// /// Asymmetric, non-waiting /// /// * `msg`: Message to send - pub async fn broadcast( - &mut self, - msg: &(impl serde::Serialize + Sync), - ) -> NetResult<(), C> { + pub async fn broadcast(&mut self, msg: &(impl serde::Serialize + Sync)) -> NetResult<(), C> { let my_id = self.index; - let packet : Bytes = bincode::serialize(&msg).unwrap().into(); + let packet: Bytes = bincode::serialize(&msg).unwrap().into(); let outgoing = self.connections.iter_mut().enumerate().map(|(i, conn)| { let id = if i < my_id { i } else { i + 1 } as u32; conn.send_bytes(packet.clone()) - .map_err(move |e| NetworkError::Outgoing{id, source: e}) + .map_err(move |e| NetworkError::Outgoing { id, source: e }) }); future::try_join_all(outgoing).await?; Ok(()) @@ -98,10 +94,7 @@ impl Network { /// Asymmetric, non-waiting /// /// * `msgs`: Messages to send - pub async fn unicast( - &mut self, - msgs: &[impl serde::Serialize + Sync], - ) -> NetResult<(), C> { + pub async fn unicast(&mut self, msgs: &[impl serde::Serialize + Sync]) -> NetResult<(), C> { let my_id = self.index; let outgoing = self .connections @@ -122,9 +115,7 @@ impl Network { /// Asymmetric, waiting /// /// Returns: A list sorted by the connections (skipping yourself) - pub async fn receive_all( - &mut self, - ) -> NetResult, C> { + pub async fn receive_all(&mut self) -> NetResult, C> { let my_id = self.index; let messages = self.connections.iter_mut().enumerate().map(|(i, conn)| { let msg = conn.recv::(); @@ -163,7 +154,7 @@ impl Network { let (mut tx, mut rx): (Vec<_>, Vec<_>) = self.connections.iter_mut().map(|c| c.split()).unzip(); - let packet : Bytes = bincode::serialize(&msg).unwrap().into(); + let packet: Bytes = bincode::serialize(&msg).unwrap().into(); let outgoing = tx.iter_mut().enumerate().map(|(id, conn)| { let id = if id < my_id { id } else { id + 1 } as u32; conn.send_bytes(packet.clone()) @@ -209,10 +200,7 @@ impl Network { /// will be send to party `i`. /// /// * `msg`: message to send and receive - pub async fn symmetric_unicast( - &mut self, - mut msgs: Vec, - ) -> NetResult, C> + pub async fn symmetric_unicast(&mut self, mut msgs: Vec) -> NetResult, C> where T: serde::Serialize + serde::de::DeserializeOwned + Sync, { @@ -308,7 +296,6 @@ impl Network { async fn drop_party(_id: usize) -> Result<(), ()> { todo!("Initiate a drop vote"); - } } @@ -317,7 +304,7 @@ impl Network { // // Outline: // Currently we do not handle any unprepared protocols, but only expected 'happy path' behaviour. -// In case of protocols or communication failure we return an error, but we do not provide a solution. +// In case of protocols or communication failure we return an error, but we do not provide a solution. // The current expection is for the downstream user to handle it themselves, instead of doing // something automatic. However, we currently do not have any methods for removing parties, // and if we had we still need all other parties to come to the same conclusion. @@ -341,12 +328,14 @@ impl Network { // // - impl Unicast for Network { type UnicastError = NetworkError; #[tracing::instrument(skip_all)] - async fn unicast(&mut self, msgs: &[impl serde::Serialize + Sync]) -> Result<(), Self::UnicastError> { + async fn unicast( + &mut self, + msgs: &[impl serde::Serialize + Sync], + ) -> Result<(), Self::UnicastError> { self.unicast(msgs).await } @@ -359,7 +348,9 @@ impl Unicast for Network { } #[tracing::instrument(skip_all)] - async fn receive_all(&mut self) -> Result, Self::UnicastError> { + async fn receive_all( + &mut self, + ) -> Result, Self::UnicastError> { self.receive_all().await } @@ -372,7 +363,10 @@ impl Broadcast for Network { type BroadcastError = NetworkError; #[tracing::instrument(skip_all)] - async fn broadcast(&mut self, msg: &(impl serde::Serialize + Sync)) -> Result<(), Self::BroadcastError> { + async fn broadcast( + &mut self, + msg: &(impl serde::Serialize + Sync), + ) -> Result<(), Self::BroadcastError> { self.broadcast(msg).await } @@ -408,7 +402,13 @@ impl Tuneable for Network { idx: usize, ) -> Result { let idx = self.id_to_index(idx); - self.connections[idx].recv().await.map_err(|e | NetworkError::Incoming { id: idx as u32, source: e }) + self.connections[idx] + .recv() + .await + .map_err(|e| NetworkError::Incoming { + id: idx as u32, + source: e, + }) } async fn send_to( @@ -417,7 +417,13 @@ impl Tuneable for Network { msg: &T, ) -> Result<(), Self::TuningError> { let idx = self.id_to_index(idx); - self.connections[idx].send(msg).await.map_err(|e | NetworkError::Outgoing { id: idx as u32, source: e }) + self.connections[idx] + .send(msg) + .await + .map_err(|e| NetworkError::Outgoing { + id: idx as u32, + source: e, + }) } } @@ -474,10 +480,7 @@ impl TcpNetwork { /// /// * `me`: Socket address to open to /// * `peers`: Socket addresses of other peers - pub async fn connect( - me: SocketAddr, - peers: &[SocketAddr], - ) -> NetResult { + pub async fn connect(me: SocketAddr, peers: &[SocketAddr]) -> NetResult { let n = peers.len(); // Connecting to parties diff --git a/src/protocols/cutnchoose.rs b/src/protocols/cutnchoose.rs index 3e782e6..0a1d8c7 100644 --- a/src/protocols/cutnchoose.rs +++ b/src/protocols/cutnchoose.rs @@ -90,7 +90,9 @@ mod test { use crate::{ algebra::element::Element32, net::{ - agency::Broadcast, connection::{self, ConnectionError, DuplexConnection}, Channel, RecvBytes, SendBytes, SplitChannel + agency::Broadcast, + connection::{self, ConnectionError, DuplexConnection}, + Channel, RecvBytes, SendBytes, SplitChannel, }, protocols::cutnchoose::{choose, cut}, schemes::{ @@ -157,7 +159,8 @@ mod test { fn recv_bytes( &mut self, - ) -> impl std::future::Future> + Send { + ) -> impl std::future::Future> + Send + { self.inner.recv_bytes() } } diff --git a/src/protocols/voting.rs b/src/protocols/voting.rs index b6ce2d8..1584c2f 100644 --- a/src/protocols/voting.rs +++ b/src/protocols/voting.rs @@ -10,7 +10,6 @@ pub struct Proposal { creator: usize, } - pub trait Initiative: Serialize + DeserializeOwned + Sync { type Args; fn name() -> &'static str; @@ -18,10 +17,12 @@ pub trait Initiative: Serialize + DeserializeOwned + Sync { } impl Proposal { - - pub async fn initiate(initative: T, mut coms: C) -> Result, C::BroadcastError> { + pub async fn initiate( + initative: T, + mut coms: C, + ) -> Result, C::BroadcastError> { let creator = coms.id(); - let proposal = Self {initative, creator}; + let proposal = Self { initative, creator }; let request = ProposalRequest(proposal); coms.broadcast(&request).await?; @@ -31,7 +32,6 @@ impl Proposal { pub fn execute(self, args: Args) { T::execute(args) } - } #[repr(transparent)] @@ -39,25 +39,29 @@ impl Proposal { pub struct ProposalRequest(Proposal); impl ProposalRequest { - - async fn vote(self, vote: bool, mut coms: C) -> Result>, C::BroadcastError> { + async fn vote( + self, + vote: bool, + mut coms: C, + ) -> Result>, C::BroadcastError> { let votes = coms.symmetric_broadcast(vote).await?; - let n = votes.len(); + let n = votes.len(); let yes_votes = votes.into_iter().filter(|&v| v).count(); - let res = if yes_votes > n { - Some(self.0) - } else { - None - }; + let res = if yes_votes > n { Some(self.0) } else { None }; Ok(res) } - pub async fn accept(self, coms: C) -> Result>, C::BroadcastError> { + pub async fn accept( + self, + coms: C, + ) -> Result>, C::BroadcastError> { self.vote(true, coms).await } - pub async fn reject(self, coms: C) -> Result>, C::BroadcastError>{ + pub async fn reject( + self, + coms: C, + ) -> Result>, C::BroadcastError> { self.vote(false, coms).await - } } diff --git a/src/schemes/mod.rs b/src/schemes/mod.rs index 405fe00..b529273 100644 --- a/src/schemes/mod.rs +++ b/src/schemes/mod.rs @@ -117,8 +117,6 @@ pub trait Shared: } } - - /// Support for multiplication of two shares for producing a share. /// /// Note, that this is different to beaver multiplication as it does not require @@ -147,7 +145,6 @@ pub mod interactive { use crate::net::Tuneable; - #[derive(Debug, Error)] #[error("Communication failure: {0}")] pub struct CommunicationError(Box); @@ -155,11 +152,15 @@ pub mod interactive { fn new(e: impl Error + Send + 'static) -> Self { Self(Box::new(e)) } - } use super::*; - impl InteractiveShared for S where S: Shared + Send, V: Send + Clone, C: Send + Sync + Clone { + impl InteractiveShared for S + where + S: Shared + Send, + V: Send + Clone, + C: Send + Sync + Clone, + { type Context = S::Context; type Value = V; type Error = CommunicationError; @@ -172,7 +173,9 @@ pub mod interactive { ) -> Result { let shares = S::share(ctx, secret, rng); let my_share = shares[coms.id()].clone(); - coms.unicast(&shares).await.map_err(CommunicationError::new)?; + coms.unicast(&shares) + .await + .map_err(CommunicationError::new)?; Ok(my_share) } @@ -181,7 +184,10 @@ pub mod interactive { secret: Self, mut coms: impl Communicate, ) -> Result { - let shares = coms.symmetric_broadcast(secret).await.map_err(CommunicationError::new)?; + let shares = coms + .symmetric_broadcast(secret) + .await + .map_err(CommunicationError::new)?; Ok(Shared::recombine(ctx, &shares).unwrap()) } @@ -192,7 +198,10 @@ pub mod interactive { mut coms: impl Communicate, ) -> Result, Self::Error> { let shares = S::share(ctx, secret, rng); - let shared = coms.symmetric_unicast(shares).await.map_err(CommunicationError::new)?; + let shared = coms + .symmetric_unicast(shares) + .await + .map_err(CommunicationError::new)?; Ok(shared) } @@ -207,7 +216,6 @@ pub mod interactive { } } - pub trait InteractiveShared: Sized + Add @@ -216,44 +224,48 @@ pub mod interactive { + serde::de::DeserializeOwned + Clone + Sync - { - type Context: Sync + Send + Clone; - type Value: Clone + Send; - type Error: Send + Sized + 'static; + { + type Context: Sync + Send + Clone; + type Value: Clone + Send; + type Error: Send + Sized + 'static; - fn share( - ctx: &Self::Context, - secret: Self::Value, - rng: impl RngCore + Send, - coms: impl Communicate, - ) -> impl std::future::Future>; + fn share( + ctx: &Self::Context, + secret: Self::Value, + rng: impl RngCore + Send, + coms: impl Communicate, + ) -> impl std::future::Future>; - fn symmetric_share( - ctx: &Self::Context, - secret: Self::Value, - rng: impl RngCore + Send, - coms: impl Communicate, - ) -> impl std::future::Future, Self::Error>>; + fn symmetric_share( + ctx: &Self::Context, + secret: Self::Value, + rng: impl RngCore + Send, + coms: impl Communicate, + ) -> impl std::future::Future, Self::Error>>; - fn receive_share( - ctx: &Self::Context, - coms: impl Communicate, - from: usize, - ) -> impl std::future::Future>; + fn receive_share( + ctx: &Self::Context, + coms: impl Communicate, + from: usize, + ) -> impl std::future::Future>; - fn recombine( - ctx: &Self::Context, - secrets: Self, - coms: impl Communicate, - ) -> impl std::future::Future>; - } + fn recombine( + ctx: &Self::Context, + secrets: Self, + coms: impl Communicate, + ) -> impl std::future::Future>; + } } pub trait Verify: Sized { type Args: Send; - fn verify(&self, coms: impl Communicate, args: Self::Args) -> impl Future + Send; + fn verify(&self, coms: impl Communicate, args: Self::Args) + -> impl Future + Send; - fn verify_many(batch: &[Self], coms: impl Communicate, args: Self::Args) -> impl Future> + Send; + fn verify_many( + batch: &[Self], + coms: impl Communicate, + args: Self::Args, + ) -> impl Future> + Send; } -