use std::collections::BTreeMap;
use serde_cbor::Value;
use log::debug;
use blake2::{Blake2b, Digest, digest::consts::U32};
use crate::error::{ThinClientError, error_code_to_error};
use crate::core::ThinClient;
mod optional_bytes {
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S>(value: &Option<Vec<u8>>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match value {
Some(bytes) => serde_bytes::serialize(bytes, serializer),
None => Option::<&[u8]>::None.serialize(serializer),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
{
let opt: Option<serde_bytes::ByteBuf> = Option::deserialize(deserializer)?;
Ok(opt.map(|b| b.into_vec()))
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct NewKeypairRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
seed: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct NewKeypairReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default, with = "optional_bytes")]
write_cap: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
read_cap: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
first_message_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct EncryptReadRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
read_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
message_box_index: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct EncryptReadReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default, with = "optional_bytes")]
message_ciphertext: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
envelope_descriptor: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
envelope_hash: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
next_message_box_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct EncryptWriteRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
plaintext: Vec<u8>,
#[serde(with = "serde_bytes")]
write_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
message_box_index: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct EncryptWriteReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default, with = "optional_bytes")]
message_ciphertext: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
envelope_descriptor: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
envelope_hash: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
next_message_box_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct StartResendingEncryptedMessageRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(skip_serializing_if = "Option::is_none", with = "optional_bytes")]
read_cap: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none", with = "optional_bytes")]
write_cap: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none", with = "optional_bytes")]
message_box_index: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none")]
reply_index: Option<u8>,
#[serde(with = "serde_bytes")]
envelope_descriptor: Vec<u8>,
#[serde(with = "serde_bytes")]
message_ciphertext: Vec<u8>,
#[serde(with = "serde_bytes")]
envelope_hash: Vec<u8>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
no_retry_on_box_id_not_found: bool,
#[serde(skip_serializing_if = "std::ops::Not::not")]
no_idempotent_box_already_exists: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct StartResendingEncryptedMessageReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default, with = "optional_bytes")]
plaintext: Option<Vec<u8>>,
error_code: u8,
#[serde(default, with = "optional_bytes")]
courier_identity_hash: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
courier_queue_id: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
pub struct StartResendingResult {
pub plaintext: Vec<u8>,
pub courier_identity_hash: Option<Vec<u8>>,
pub courier_queue_id: Option<Vec<u8>>,
}
#[derive(Debug, Clone, serde::Serialize)]
struct WriteStreamRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
write_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
start_index: Vec<u8>,
#[serde(with = "serde_bytes")]
payload: Vec<u8>,
window: i64,
}
#[derive(Debug, Clone, serde::Deserialize)]
struct WriteStreamReply {
#[serde(default, with = "optional_bytes")]
next_message_box_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
#[serde(default)]
box_count: u32,
}
#[derive(Debug, Clone, serde::Serialize)]
struct ReadStreamRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
read_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
start_index: Vec<u8>,
box_count: u32,
window: i64,
}
#[derive(Debug, Clone, serde::Deserialize)]
struct ReadStreamReply {
#[serde(default, with = "optional_bytes")]
payload: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
next_message_box_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
#[serde(default)]
box_count: u32,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CancelResendingEncryptedMessageRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
envelope_hash: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CancelResendingEncryptedMessageReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct NextMessageBoxIndexRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
message_box_index: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct NextMessageBoxIndexReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default, with = "optional_bytes")]
next_message_box_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct GetMessageBoxIndexCounterRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
message_box_index: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct GetMessageBoxIndexCounterReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(default)]
counter: u64,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct StartResendingCopyCommandRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
write_cap: Vec<u8>,
#[serde(skip_serializing_if = "Option::is_none", default, with = "optional_bytes")]
courier_identity_hash: Option<Vec<u8>>,
#[serde(skip_serializing_if = "Option::is_none", default, with = "optional_bytes")]
courier_queue_id: Option<Vec<u8>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct StartResendingCopyCommandReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
error_code: u8,
#[serde(default)]
replica_error_code: u8,
#[serde(default)]
failed_envelope_index: u64,
}
const THIN_CLIENT_ERROR_COPY_COMMAND_FAILED: u8 = 26;
fn copy_reply_to_error(reply: &StartResendingCopyCommandReply) -> Option<ThinClientError> {
match reply.error_code {
0 => None,
THIN_CLIENT_ERROR_COPY_COMMAND_FAILED => Some(ThinClientError::CopyCommandFailed {
replica_error_code: reply.replica_error_code,
failed_envelope_index: reply.failed_envelope_index,
}),
code => Some(error_code_to_error(code)),
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CancelResendingCopyCommandRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
write_cap_hash: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CancelResendingCopyCommandReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromPayloadRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
payload: Vec<u8>,
#[serde(with = "serde_bytes")]
dest_write_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
dest_start_index: Vec<u8>,
is_start: bool,
is_last: bool,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromPayloadReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
envelopes: Option<Vec<serde_bytes::ByteBuf>>,
#[serde(default, with = "optional_bytes")]
next_dest_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct EnvelopeDestination {
#[serde(with = "serde_bytes")]
payload: Vec<u8>,
#[serde(with = "serde_bytes")]
write_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
start_index: Vec<u8>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromPayloadsRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
destinations: Vec<EnvelopeDestination>,
is_start: bool,
is_last: bool,
#[serde(default, with = "optional_bytes")]
buffer: Option<Vec<u8>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromPayloadsReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
envelopes: Option<Vec<serde_bytes::ByteBuf>>,
#[serde(default, with = "optional_bytes")]
buffer: Option<Vec<u8>>,
#[serde(default)]
next_dest_indices: Option<Vec<serde_bytes::ByteBuf>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromTombstoneRangeRequest {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
#[serde(with = "serde_bytes")]
dest_write_cap: Vec<u8>,
#[serde(with = "serde_bytes")]
dest_start_index: Vec<u8>,
max_count: u32,
is_start: bool,
is_last: bool,
#[serde(default, with = "optional_bytes")]
buffer: Option<Vec<u8>>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
struct CreateCourierEnvelopesFromTombstoneRangeReply {
#[serde(with = "serde_bytes")]
query_id: Vec<u8>,
envelopes: Option<Vec<serde_bytes::ByteBuf>>,
#[serde(default, with = "optional_bytes")]
buffer: Option<Vec<u8>>,
#[serde(default, with = "optional_bytes")]
next_dest_index: Option<Vec<u8>>,
#[serde(default)]
error_code: u8,
}
#[derive(Debug, Clone)]
pub struct KeypairResult {
pub write_cap: Vec<u8>,
pub read_cap: Vec<u8>,
pub first_message_index: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct EncryptReadResult {
pub message_ciphertext: Vec<u8>,
pub envelope_descriptor: Vec<u8>,
pub envelope_hash: [u8; 32],
pub next_message_box_index: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct EncryptWriteResult {
pub message_ciphertext: Vec<u8>,
pub envelope_descriptor: Vec<u8>,
pub envelope_hash: [u8; 32],
pub next_message_box_index: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct CreateEnvelopesResult {
pub envelopes: Vec<Vec<u8>>,
pub buffer: Vec<u8>,
pub next_dest_index: Option<Vec<u8>>,
pub next_dest_indices: Option<Vec<Vec<u8>>>,
}
impl ThinClient {
pub async fn new_keypair(&self, seed: &[u8; 32]) -> Result<KeypairResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = NewKeypairRequest {
query_id: query_id.clone(),
seed: seed.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("new_keypair".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: NewKeypairReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("new_keypair failed with error code: {}", reply.error_code)));
}
let write_cap = reply.write_cap.ok_or_else(|| ThinClientError::Other("new_keypair: write_cap is None".to_string()))?;
let read_cap = reply.read_cap.ok_or_else(|| ThinClientError::Other("new_keypair: read_cap is None".to_string()))?;
let first_message_index = reply.first_message_index.ok_or_else(|| ThinClientError::Other("new_keypair: first_message_index is None".to_string()))?;
Ok(KeypairResult { write_cap, read_cap, first_message_index })
}
pub async fn encrypt_read(
&self,
read_cap: &[u8],
message_box_index: &[u8]
) -> Result<EncryptReadResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = EncryptReadRequest {
query_id: query_id.clone(),
read_cap: read_cap.to_vec(),
message_box_index: message_box_index.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("encrypt_read".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: EncryptReadReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("encrypt_read failed with error code: {}", reply.error_code)));
}
let message_ciphertext = reply.message_ciphertext.ok_or_else(|| ThinClientError::Other("encrypt_read: message_ciphertext is None".to_string()))?;
let envelope_descriptor = reply.envelope_descriptor.ok_or_else(|| ThinClientError::Other("encrypt_read: envelope_descriptor is None".to_string()))?;
let envelope_hash_vec = reply.envelope_hash.ok_or_else(|| ThinClientError::Other("encrypt_read: envelope_hash is None".to_string()))?;
let next_message_box_index = reply.next_message_box_index.ok_or_else(|| ThinClientError::Other("encrypt_read: next_message_box_index is None".to_string()))?;
let mut envelope_hash = [0u8; 32];
envelope_hash.copy_from_slice(&envelope_hash_vec[..32]);
Ok(EncryptReadResult {
message_ciphertext,
envelope_descriptor,
envelope_hash,
next_message_box_index,
})
}
pub async fn encrypt_write(
&self,
plaintext: &[u8],
write_cap: &[u8],
message_box_index: &[u8]
) -> Result<EncryptWriteResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = EncryptWriteRequest {
query_id: query_id.clone(),
plaintext: plaintext.to_vec(),
write_cap: write_cap.to_vec(),
message_box_index: message_box_index.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("encrypt_write".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: EncryptWriteReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("encrypt_write failed with error code: {}", reply.error_code)));
}
let message_ciphertext = reply.message_ciphertext.ok_or_else(|| ThinClientError::Other("encrypt_write: message_ciphertext is None".to_string()))?;
let envelope_descriptor = reply.envelope_descriptor.ok_or_else(|| ThinClientError::Other("encrypt_write: envelope_descriptor is None".to_string()))?;
let envelope_hash_vec = reply.envelope_hash.ok_or_else(|| ThinClientError::Other("encrypt_write: envelope_hash is None".to_string()))?;
let next_message_box_index = reply.next_message_box_index.ok_or_else(|| ThinClientError::Other("encrypt_write: next_message_box_index is None".to_string()))?;
let mut envelope_hash = [0u8; 32];
envelope_hash.copy_from_slice(&envelope_hash_vec[..32]);
Ok(EncryptWriteResult {
message_ciphertext,
envelope_descriptor,
envelope_hash,
next_message_box_index,
})
}
pub async fn start_resending_encrypted_message(
&self,
read_cap: Option<&[u8]>,
write_cap: Option<&[u8]>,
message_box_index: Option<&[u8]>,
reply_index: Option<u8>,
envelope_descriptor: &[u8],
message_ciphertext: &[u8],
envelope_hash: &[u8; 32]
) -> Result<StartResendingResult, ThinClientError> {
self.start_resending_encrypted_message_with_options(
read_cap,
write_cap,
message_box_index,
reply_index,
envelope_descriptor,
message_ciphertext,
envelope_hash,
false,
false,
).await
}
pub async fn start_resending_encrypted_message_return_box_exists(
&self,
read_cap: Option<&[u8]>,
write_cap: Option<&[u8]>,
message_box_index: Option<&[u8]>,
reply_index: Option<u8>,
envelope_descriptor: &[u8],
message_ciphertext: &[u8],
envelope_hash: &[u8; 32]
) -> Result<StartResendingResult, ThinClientError> {
self.start_resending_encrypted_message_with_options(
read_cap,
write_cap,
message_box_index,
reply_index,
envelope_descriptor,
message_ciphertext,
envelope_hash,
false,
true, ).await
}
pub async fn start_resending_encrypted_message_no_retry(
&self,
read_cap: Option<&[u8]>,
write_cap: Option<&[u8]>,
message_box_index: Option<&[u8]>,
reply_index: Option<u8>,
envelope_descriptor: &[u8],
message_ciphertext: &[u8],
envelope_hash: &[u8; 32]
) -> Result<StartResendingResult, ThinClientError> {
self.start_resending_encrypted_message_with_options(
read_cap,
write_cap,
message_box_index,
reply_index,
envelope_descriptor,
message_ciphertext,
envelope_hash,
true, false,
).await
}
async fn start_resending_encrypted_message_with_options(
&self,
read_cap: Option<&[u8]>,
write_cap: Option<&[u8]>,
message_box_index: Option<&[u8]>,
reply_index: Option<u8>,
envelope_descriptor: &[u8],
message_ciphertext: &[u8],
envelope_hash: &[u8; 32],
no_retry_on_box_id_not_found: bool,
no_idempotent_box_already_exists: bool,
) -> Result<StartResendingResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = StartResendingEncryptedMessageRequest {
query_id: query_id.clone(),
read_cap: read_cap.map(|rc| rc.to_vec()),
write_cap: write_cap.map(|wc| wc.to_vec()),
message_box_index: message_box_index.map(|mbi| mbi.to_vec()),
reply_index,
envelope_descriptor: envelope_descriptor.to_vec(),
message_ciphertext: message_ciphertext.to_vec(),
envelope_hash: envelope_hash.to_vec(),
no_retry_on_box_id_not_found,
no_idempotent_box_already_exists,
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("start_resending_encrypted_message".to_string()), request_value);
let tracking_key = envelope_hash.to_vec();
self.in_flight_resends.lock().await.insert(tracking_key.clone(), request.clone());
let reply_map = match self.send_and_wait_direct(query_id, request).await {
Ok(reply) => {
self.in_flight_resends.lock().await.remove(&tracking_key);
reply
}
Err(e) => {
self.in_flight_resends.lock().await.remove(&tracking_key);
return Err(e);
}
};
let reply: StartResendingEncryptedMessageReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
debug!("start_resending_encrypted_message: received reply, error_code={}, plaintext_len={}",
reply.error_code, reply.plaintext.as_ref().map(|p| p.len()).unwrap_or(0));
if reply.error_code != 0 {
return Err(error_code_to_error(reply.error_code));
}
Ok(StartResendingResult {
plaintext: reply.plaintext.unwrap_or_default(),
courier_identity_hash: reply.courier_identity_hash,
courier_queue_id: reply.courier_queue_id,
})
}
pub async fn write_stream(
&self,
write_cap: &[u8],
start_index: &[u8],
payload: &[u8],
window: i64,
) -> Result<Vec<u8>, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = WriteStreamRequest {
query_id: query_id.clone(),
write_cap: write_cap.to_vec(),
start_index: start_index.to_vec(),
payload: payload.to_vec(),
window,
};
let request_value =
serde_cbor::value::to_value(&request_inner).map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("write_stream".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: WriteStreamReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
debug!("write_stream: received reply, error_code={}, boxes={}", reply.error_code, reply.box_count);
if reply.error_code != 0 {
return Err(error_code_to_error(reply.error_code));
}
Ok(reply.next_message_box_index.unwrap_or_default())
}
pub async fn read_stream(
&self,
read_cap: &[u8],
start_index: &[u8],
box_count: u32,
window: i64,
) -> Result<(Vec<u8>, Vec<u8>), ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = ReadStreamRequest {
query_id: query_id.clone(),
read_cap: read_cap.to_vec(),
start_index: start_index.to_vec(),
box_count,
window,
};
let request_value =
serde_cbor::value::to_value(&request_inner).map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("read_stream".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: ReadStreamReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
debug!("read_stream: received reply, error_code={}, boxes={}", reply.error_code, reply.box_count);
if reply.error_code != 0 {
return Err(error_code_to_error(reply.error_code));
}
Ok((
reply.payload.unwrap_or_default(),
reply.next_message_box_index.unwrap_or_default(),
))
}
pub async fn cancel_resending_encrypted_message(&self, envelope_hash: &[u8; 32]) -> Result<(), ThinClientError> {
self.in_flight_resends.lock().await.remove(&envelope_hash.to_vec());
if !self.is_connected() {
return Ok(());
}
let query_id = Self::new_query_id();
let request_inner = CancelResendingEncryptedMessageRequest {
query_id: query_id.clone(),
envelope_hash: envelope_hash.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("cancel_resending_encrypted_message".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: CancelResendingEncryptedMessageReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("cancel_resending_encrypted_message failed with error code: {}", reply.error_code)));
}
Ok(())
}
pub async fn next_message_box_index(&self, message_box_index: &[u8]) -> Result<Vec<u8>, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = NextMessageBoxIndexRequest {
query_id: query_id.clone(),
message_box_index: message_box_index.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("next_message_box_index".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: NextMessageBoxIndexReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("next_message_box_index failed with error code: {}", reply.error_code)));
}
let next_index = reply.next_message_box_index.ok_or_else(|| ThinClientError::Other("next_message_box_index: next_message_box_index is None".to_string()))?;
Ok(next_index)
}
pub async fn get_message_box_index_counter(&self, message_box_index: &[u8]) -> Result<u64, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = GetMessageBoxIndexCounterRequest {
query_id: query_id.clone(),
message_box_index: message_box_index.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("get_message_box_index_counter".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: GetMessageBoxIndexCounterReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("get_message_box_index_counter failed with error code: {}", reply.error_code)));
}
Ok(reply.counter)
}
pub async fn start_resending_copy_command(
&self,
write_cap: &[u8],
courier_identity_hash: Option<&[u8]>,
courier_queue_id: Option<&[u8]>
) -> Result<(), ThinClientError> {
let tracking_key = Blake2b::<U32>::digest(write_cap).to_vec();
let query_id = Self::new_query_id();
let request_inner = StartResendingCopyCommandRequest {
query_id: query_id.clone(),
write_cap: write_cap.to_vec(),
courier_identity_hash: courier_identity_hash.map(|h| h.to_vec()),
courier_queue_id: courier_queue_id.map(|q| q.to_vec()),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("start_resending_copy_command".to_string()), request_value);
self.in_flight_resends.lock().await.insert(tracking_key.clone(), request.clone());
let reply_map = match self.send_and_wait_direct(query_id, request).await {
Ok(reply) => {
self.in_flight_resends.lock().await.remove(&tracking_key);
reply
}
Err(e) => {
self.in_flight_resends.lock().await.remove(&tracking_key);
return Err(e);
}
};
let reply: StartResendingCopyCommandReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if let Some(err) = copy_reply_to_error(&reply) {
return Err(err);
}
Ok(())
}
pub async fn cancel_resending_copy_command(&self, write_cap_hash: &[u8; 32]) -> Result<(), ThinClientError> {
self.in_flight_resends.lock().await.remove(&write_cap_hash.to_vec());
if !self.is_connected() {
return Ok(());
}
let query_id = Self::new_query_id();
let request_inner = CancelResendingCopyCommandRequest {
query_id: query_id.clone(),
write_cap_hash: write_cap_hash.to_vec(),
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("cancel_resending_copy_command".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: CancelResendingCopyCommandReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("cancel_resending_copy_command failed with error code: {}", reply.error_code)));
}
Ok(())
}
pub async fn create_courier_envelopes_from_payload(
&self,
payload: &[u8],
dest_write_cap: &[u8],
dest_start_index: &[u8],
is_start: bool,
is_last: bool
) -> Result<CreateEnvelopesResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = CreateCourierEnvelopesFromPayloadRequest {
query_id: query_id.clone(),
payload: payload.to_vec(),
dest_write_cap: dest_write_cap.to_vec(),
dest_start_index: dest_start_index.to_vec(),
is_start,
is_last,
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("create_courier_envelopes_from_payload".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: CreateCourierEnvelopesFromPayloadReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("create_courier_envelopes_from_payload failed with error code: {}", reply.error_code)));
}
Ok(CreateEnvelopesResult {
envelopes: reply.envelopes.unwrap_or_default().into_iter().map(|b| b.into_vec()).collect(),
buffer: Vec::new(),
next_dest_index: reply.next_dest_index,
next_dest_indices: None,
})
}
pub async fn create_courier_envelopes_from_multi_payload(
&self,
destinations: Vec<(&[u8], &[u8], &[u8])>,
is_start: bool,
is_last: bool,
buffer: Option<Vec<u8>>,
) -> Result<CreateEnvelopesResult, ThinClientError> {
let query_id = Self::new_query_id();
let destinations_inner: Vec<EnvelopeDestination> = destinations
.into_iter()
.map(|(payload, write_cap, start_index)| EnvelopeDestination {
payload: payload.to_vec(),
write_cap: write_cap.to_vec(),
start_index: start_index.to_vec(),
})
.collect();
let request_inner = CreateCourierEnvelopesFromPayloadsRequest {
query_id: query_id.clone(),
destinations: destinations_inner,
is_start,
is_last,
buffer,
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("create_courier_envelopes_from_multi_payload".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: CreateCourierEnvelopesFromPayloadsReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("create_courier_envelopes_from_multi_payload failed with error code: {}", reply.error_code)));
}
Ok(CreateEnvelopesResult {
envelopes: reply.envelopes.unwrap_or_default().into_iter().map(|b| b.into_vec()).collect(),
buffer: reply.buffer.unwrap_or_default(),
next_dest_index: None,
next_dest_indices: reply.next_dest_indices.map(|v| v.into_iter().map(|b| b.into_vec()).collect()),
})
}
pub async fn create_courier_envelopes_from_tombstone_range(
&self,
dest_write_cap: &[u8],
dest_start_index: &[u8],
max_count: u32,
is_start: bool,
is_last: bool,
buffer: Option<Vec<u8>>,
) -> Result<CreateEnvelopesResult, ThinClientError> {
let query_id = Self::new_query_id();
let request_inner = CreateCourierEnvelopesFromTombstoneRangeRequest {
query_id: query_id.clone(),
dest_write_cap: dest_write_cap.to_vec(),
dest_start_index: dest_start_index.to_vec(),
max_count,
is_start,
is_last,
buffer,
};
let request_value = serde_cbor::value::to_value(&request_inner)
.map_err(|e| ThinClientError::CborError(e))?;
let mut request = BTreeMap::new();
request.insert(Value::Text("create_courier_envelopes_from_tombstone_range".to_string()), request_value);
let reply_map = self.send_and_wait_direct(query_id, request).await?;
let reply: CreateCourierEnvelopesFromTombstoneRangeReply = serde_cbor::value::from_value(Value::Map(reply_map))
.map_err(|e| ThinClientError::CborError(e))?;
if reply.error_code != 0 {
return Err(ThinClientError::Other(format!("create_courier_envelopes_from_tombstone_range failed with error code: {}", reply.error_code)));
}
Ok(CreateEnvelopesResult {
envelopes: reply.envelopes.unwrap_or_default().into_iter().map(|b| b.into_vec()).collect(),
buffer: reply.buffer.unwrap_or_default(),
next_dest_index: reply.next_dest_index,
next_dest_indices: None,
})
}
}
#[derive(Debug, Clone)]
pub struct TombstoneEnvelope {
pub message_ciphertext: Vec<u8>,
pub envelope_descriptor: Vec<u8>,
pub envelope_hash: Vec<u8>,
pub box_index: Vec<u8>,
}
#[derive(Debug)]
pub struct TombstoneRangeResult {
pub envelopes: Vec<TombstoneEnvelope>,
pub next: Vec<u8>,
pub error: Option<String>,
}
impl ThinClient {
pub async fn tombstone_range(
&self,
write_cap: &[u8],
start: &[u8],
max_count: u32
) -> TombstoneRangeResult {
if max_count == 0 {
return TombstoneRangeResult {
envelopes: Vec::new(),
next: start.to_vec(),
error: None,
};
}
let mut cur = start.to_vec();
let mut envelopes: Vec<TombstoneEnvelope> = Vec::with_capacity(max_count as usize);
while (envelopes.len() as u32) < max_count {
match self.encrypt_write(&[], write_cap, &cur).await {
Ok(result) => {
envelopes.push(TombstoneEnvelope {
message_ciphertext: result.message_ciphertext,
envelope_descriptor: result.envelope_descriptor,
envelope_hash: result.envelope_hash.to_vec(),
box_index: cur.clone(),
});
cur = result.next_message_box_index;
}
Err(e) => {
let count = envelopes.len();
return TombstoneRangeResult {
envelopes,
next: cur,
error: Some(format!("Error creating tombstone at index {}: {:?}", count, e)),
};
}
}
}
TombstoneRangeResult {
envelopes,
next: cur,
error: None,
}
}
}
#[cfg(test)]
mod sack_request_tests {
use super::*;
fn map_keys(value: Value) -> Vec<String> {
match value {
Value::Map(m) => m
.into_keys()
.filter_map(|k| match k {
Value::Text(s) => Some(s),
_ => None,
})
.collect(),
_ => panic!("expected a CBOR map"),
}
}
#[test]
fn write_stream_request_field_names() {
let req = WriteStreamRequest {
query_id: vec![1, 2, 3],
write_cap: vec![4, 5],
start_index: vec![6, 7],
payload: vec![8, 9],
window: 16,
};
let keys = map_keys(serde_cbor::value::to_value(&req).unwrap());
for expected in ["query_id", "write_cap", "start_index", "payload", "window"] {
assert!(keys.iter().any(|k| k == expected), "missing field {expected}, got {keys:?}");
}
}
#[test]
fn read_stream_request_field_names() {
let req = ReadStreamRequest {
query_id: vec![1],
read_cap: vec![2],
start_index: vec![3],
box_count: 4,
window: 0,
};
let keys = map_keys(serde_cbor::value::to_value(&req).unwrap());
for expected in ["query_id", "read_cap", "start_index", "box_count", "window"] {
assert!(keys.iter().any(|k| k == expected), "missing field {expected}, got {keys:?}");
}
}
}