mod blob;
mod cmd;
mod data;
mod duty;
mod errors;
mod map;
mod network;
mod query;
mod sender;
mod sequence;
mod transfer;
mod wire_msg;
pub use self::{
blob::{BlobRead, BlobWrite},
cmd::Cmd,
data::{DataCmd, DataQuery},
duty::{AdultDuties, Duty, ElderDuties, NodeDuties},
errors::{Error, Result},
map::{MapRead, MapWrite},
network::{
NodeCmd, NodeCmdError, NodeDataCmd, NodeDataError, NodeDataQuery, NodeDataQueryResponse,
NodeEvent, NodeQuery, NodeQueryResponse, NodeRewardError, NodeRewardQuery,
NodeRewardQueryResponse, NodeSystemCmd, NodeTransferCmd, NodeTransferError,
NodeTransferQuery, NodeTransferQueryResponse,
},
query::Query,
sender::{Address, MsgSender, TransientElderKey, TransientSectionKey},
sequence::{SequenceRead, SequenceWrite},
transfer::{TransferCmd, TransferQuery},
};
use crate::errors::ErrorDebug;
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use sn_data_types::{
ActorHistory, AppPermissions, Blob, Map, MapEntries, MapPermissionSet, MapValue, MapValues,
Money, PublicKey, ReplicaPublicKeySet, Sequence, SequenceEntries, SequenceEntry,
SequencePermissions, SequencePrivatePolicy, SequencePublicPolicy, Signature,
TransferAgreementProof, TransferValidated,
};
use std::{
collections::{BTreeMap, BTreeSet},
convert::TryFrom,
fmt,
};
use wire_msg::WireMsg;
use xor_name::XorName;
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub struct MsgEnvelope {
pub message: Message,
pub origin: MsgSender,
pub proxies: Vec<MsgSender>,
}
impl MsgEnvelope {
pub fn id(&self) -> MessageId {
self.message.id()
}
pub fn verify(&self) -> Result<bool> {
let data = if self.proxies.is_empty() {
self.message.serialize()?
} else {
let mut msg = self.clone();
let _ = msg.proxies.pop();
msg.serialize()?
};
let sender = self.most_recent_sender();
Ok(sender.verify(&data))
}
pub fn from(bytes: Bytes) -> Result<Self> {
WireMsg::deserialize_msg(bytes)
}
pub fn serialize(&self) -> Result<Bytes> {
WireMsg::serialize_msg(self)
}
pub fn add_proxy(&mut self, proxy: MsgSender) {
self.proxies.push(proxy);
}
pub fn most_recent_sender(&self) -> &MsgSender {
match self.proxies.last() {
None => &self.origin,
Some(proxy) => proxy,
}
}
pub fn destination(&self) -> Result<Address> {
use Address::*;
use Message::*;
match &self.message {
Cmd { cmd, .. } => self.cmd_dst(cmd),
Query { query, .. } => Ok(Section(query.dst_address())),
Event { event, .. } => Ok(Client(event.dst_address())),
QueryResponse { query_origin, .. } => Ok(query_origin.clone()),
CmdError { cmd_origin, .. } => Ok(cmd_origin.clone()),
NodeCmd { cmd, .. } => Ok(cmd.dst_address()),
NodeEvent { event, .. } => Ok(event.dst_address()),
NodeQuery { query, .. } => Ok(query.dst_address()),
NodeCmdError { cmd_origin, .. } => Ok(cmd_origin.clone()),
NodeQueryResponse { query_origin, .. } => Ok(query_origin.clone()),
}
}
fn cmd_dst(&self, cmd: &Cmd) -> Result<Address> {
use Address::*;
use Cmd::*;
match cmd {
Transfer(c) => Ok(Section(c.dst_address())),
Data { cmd, payment } => {
let sender = self.most_recent_sender();
match sender.address() {
Client(xorname) => Ok(Section(xorname)),
Node(_) => {
match sender.duty() {
Some(Duty::Elder(ElderDuties::Gateway)) => {
Ok(Section(payment.sender().into()))
}
Some(Duty::Elder(ElderDuties::Transfer))
| Some(Duty::Elder(ElderDuties::Metadata)) => {
Ok(Section(cmd.dst_address()))
}
_ => Err(Error::NoSuchRecipient),
}
}
Section(_) => {
match sender.duty() {
Some(Duty::Elder(ElderDuties::Transfer))
| Some(Duty::Elder(ElderDuties::Metadata)) => {
Ok(Section(cmd.dst_address()))
}
_ => Err(Error::NoSuchRecipient),
}
}
}
}
}
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum Message {
Cmd {
cmd: Cmd,
id: MessageId,
},
Query {
query: Query,
id: MessageId,
},
Event {
event: Event,
id: MessageId,
correlation_id: MessageId,
},
QueryResponse {
response: QueryResponse,
id: MessageId,
correlation_id: MessageId,
query_origin: Address,
},
CmdError {
error: CmdError,
id: MessageId,
correlation_id: MessageId,
cmd_origin: Address,
},
NodeCmd {
cmd: NodeCmd,
id: MessageId,
},
NodeCmdError {
error: NodeCmdError,
id: MessageId,
correlation_id: MessageId,
cmd_origin: Address,
},
NodeEvent {
event: NodeEvent,
id: MessageId,
correlation_id: MessageId,
},
NodeQuery {
query: NodeQuery,
id: MessageId,
},
NodeQueryResponse {
response: NodeQueryResponse,
id: MessageId,
correlation_id: MessageId,
query_origin: Address,
},
}
impl Message {
pub fn id(&self) -> MessageId {
match self {
Self::Cmd { id, .. }
| Self::Query { id, .. }
| Self::Event { id, .. }
| Self::QueryResponse { id, .. }
| Self::CmdError { id, .. }
| Self::NodeCmd { id, .. }
| Self::NodeEvent { id, .. }
| Self::NodeQuery { id, .. }
| Self::NodeCmdError { id, .. }
| Self::NodeQueryResponse { id, .. } => *id,
}
}
pub fn serialize(&self) -> Result<Bytes> {
let payload_vec = rmp_serde::to_vec_named(&self).map_err(|err| {
Error::Serialization(format!(
"Could not serialize message payload (id: {}) with Msgpack: {}",
self.id(),
err
))
})?;
Ok(Bytes::from(payload_vec))
}
}
#[derive(Ord, PartialOrd, Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Hash)]
pub struct MessageId(pub XorName);
use tiny_keccak::sha3_256;
impl MessageId {
pub fn new() -> Self {
Self(XorName::random())
}
pub fn in_response_to(src: &MessageId) -> MessageId {
let mut hash_bytes = Vec::new();
let src = src.0;
hash_bytes.extend_from_slice(&src.0);
MessageId(XorName(sha3_256(&hash_bytes)))
}
pub fn combine(srcs: Vec<XorName>) -> MessageId {
let mut hash_bytes = Vec::new();
for src in srcs.into_iter() {
hash_bytes.extend_from_slice(&src.0);
}
MessageId(XorName(sha3_256(&hash_bytes)))
}
}
impl Default for MessageId {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for MessageId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum CmdError {
Auth(Error),
Data(Error),
Transfer(TransferError),
}
#[derive(Debug, Hash, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum TransferError {
TransferValidation(Error),
TransferRegistration(Error),
}
#[allow(clippy::large_enum_variant, clippy::type_complexity)]
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum Event {
TransferValidated {
client: XorName,
event: TransferValidated,
},
TransferAgreementReached {
client: XorName,
proof: TransferAgreementProof,
},
}
impl Event {
pub fn dst_address(&self) -> XorName {
use Event::*;
match self {
TransferValidated { client, .. } => *client,
TransferAgreementReached { client, .. } => *client,
}
}
}
#[allow(clippy::large_enum_variant, clippy::type_complexity)]
#[derive(Eq, PartialEq, Clone, Serialize, Deserialize)]
pub enum QueryResponse {
GetBlob(Result<Blob>),
GetMap(Result<Map>),
GetMapShell(Result<Map>),
GetMapVersion(Result<u64>),
ListMapEntries(Result<MapEntries>),
ListMapKeys(Result<BTreeSet<Vec<u8>>>),
ListMapValues(Result<MapValues>),
ListMapUserPermissions(Result<MapPermissionSet>),
ListMapPermissions(Result<BTreeMap<PublicKey, MapPermissionSet>>),
GetMapValue(Result<MapValue>),
GetSequence(Result<Sequence>),
GetSequenceOwner(Result<PublicKey>),
GetSequenceRange(Result<SequenceEntries>),
GetSequenceLastEntry(Result<(u64, SequenceEntry)>),
GetSequencePublicPolicy(Result<SequencePublicPolicy>),
GetSequencePrivatePolicy(Result<SequencePrivatePolicy>),
GetSequenceUserPermissions(Result<SequencePermissions>),
GetReplicaKeys(Result<ReplicaPublicKeySet>),
GetBalance(Result<Money>),
GetHistory(Result<ActorHistory>),
GetStoreCost(Result<Money>),
GetAccount(Result<(Vec<u8>, Signature)>),
ListAuthKeysAndVersion(Result<(BTreeMap<PublicKey, AppPermissions>, u64)>),
}
pub enum AuthorisationKind {
Data(DataAuthKind),
Money(MoneyAuthKind),
Misc(MiscAuthKind),
None,
}
pub enum DataAuthKind {
PublicRead,
PrivateRead,
Write,
}
pub enum MoneyAuthKind {
ReadBalance,
ReadHistory,
Transfer,
}
pub enum MiscAuthKind {
ManageAppKeys,
WriteAndTransfer,
}
#[derive(Debug, PartialEq)]
#[allow(clippy::large_enum_variant)]
pub enum TryFromError {
WrongType,
Response(Error),
}
macro_rules! try_from {
($ok_type:ty, $($variant:ident),*) => {
impl TryFrom<QueryResponse> for $ok_type {
type Error = TryFromError;
fn try_from(response: QueryResponse) -> std::result::Result<Self, Self::Error> {
match response {
$(
QueryResponse::$variant(Ok(data)) => Ok(data),
QueryResponse::$variant(Err(error)) => Err(TryFromError::Response(error)),
)*
_ => Err(TryFromError::WrongType),
}
}
}
};
}
try_from!(Blob, GetBlob);
try_from!(Map, GetMap, GetMapShell);
try_from!(u64, GetMapVersion);
try_from!(MapEntries, ListMapEntries);
try_from!(BTreeSet<Vec<u8>>, ListMapKeys);
try_from!(MapValues, ListMapValues);
try_from!(MapPermissionSet, ListMapUserPermissions);
try_from!(BTreeMap<PublicKey, MapPermissionSet>, ListMapPermissions);
try_from!(MapValue, GetMapValue);
try_from!(Sequence, GetSequence);
try_from!(PublicKey, GetSequenceOwner);
try_from!(SequenceEntries, GetSequenceRange);
try_from!((u64, SequenceEntry), GetSequenceLastEntry);
try_from!(SequencePublicPolicy, GetSequencePublicPolicy);
try_from!(SequencePrivatePolicy, GetSequencePrivatePolicy);
try_from!(SequencePermissions, GetSequenceUserPermissions);
try_from!(Money, GetBalance);
try_from!(ReplicaPublicKeySet, GetReplicaKeys);
try_from!(ActorHistory, GetHistory);
try_from!(
(BTreeMap<PublicKey, AppPermissions>, u64),
ListAuthKeysAndVersion
);
try_from!((Vec<u8>, Signature), GetAccount);
impl fmt::Debug for QueryResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use QueryResponse::*;
match self {
GetBlob(res) => write!(f, "QueryResponse::GetBlob({:?})", ErrorDebug(res)),
GetMap(res) => write!(f, "QueryResponse::GetMap({:?})", ErrorDebug(res)),
GetMapShell(res) => write!(f, "QueryResponse::GetMapShell({:?})", ErrorDebug(res)),
GetMapVersion(res) => write!(f, "QueryResponse::GetMapVersion({:?})", ErrorDebug(res)),
ListMapEntries(res) => {
write!(f, "QueryResponse::ListMapEntries({:?})", ErrorDebug(res))
}
ListMapKeys(res) => write!(f, "QueryResponse::ListMapKeys({:?})", ErrorDebug(res)),
ListMapValues(res) => write!(f, "QueryResponse::ListMapValues({:?})", ErrorDebug(res)),
ListMapPermissions(res) => write!(
f,
"QueryResponse::ListMapPermissions({:?})",
ErrorDebug(res)
),
ListMapUserPermissions(res) => write!(
f,
"QueryResponse::ListMapUserPermissions({:?})",
ErrorDebug(res)
),
GetMapValue(res) => write!(f, "QueryResponse::GetMapValue({:?})", ErrorDebug(res)),
GetSequence(res) => write!(f, "QueryResponse::GetSequence({:?})", ErrorDebug(res)),
GetSequenceRange(res) => {
write!(f, "QueryResponse::GetSequenceRange({:?})", ErrorDebug(res))
}
GetSequenceLastEntry(res) => write!(
f,
"QueryResponse::GetSequenceLastEntry({:?})",
ErrorDebug(res)
),
GetSequenceUserPermissions(res) => write!(
f,
"QueryResponse::GetSequenceUserPermissions({:?})",
ErrorDebug(res)
),
GetSequencePublicPolicy(res) => write!(
f,
"QueryResponse::GetSequencePublicPolicy({:?})",
ErrorDebug(res)
),
GetSequencePrivatePolicy(res) => write!(
f,
"QueryResponse::GetSequencePrivatePolicy({:?})",
ErrorDebug(res)
),
GetSequenceOwner(res) => {
write!(f, "QueryResponse::GetSequenceOwner({:?})", ErrorDebug(res))
}
GetReplicaKeys(res) => {
write!(f, "QueryResponse::GetReplicaKeys({:?})", ErrorDebug(res))
}
GetBalance(res) => write!(f, "QueryResponse::GetBalance({:?})", ErrorDebug(res)),
GetHistory(res) => write!(f, "QueryResponse::GetHistory({:?})", ErrorDebug(res)),
GetStoreCost(res) => write!(f, "QueryResponse::GetStoreCost({:?})", ErrorDebug(res)),
GetAccount(res) => write!(f, "QueryResponse::GetAccount({:?})", ErrorDebug(res)),
ListAuthKeysAndVersion(res) => write!(
f,
"QueryResponse::ListAuthKeysAndVersion({:?})",
ErrorDebug(res)
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::{anyhow, Result};
use sn_data_types::{Keypair, PublicBlob, UnseqMap};
use std::convert::{TryFrom, TryInto};
fn gen_keypairs() -> Vec<Keypair> {
let mut rng = rand::thread_rng();
let bls_secret_key = threshold_crypto::SecretKeySet::random(1, &mut rng);
vec![
Keypair::new_ed25519(&mut rng),
Keypair::new_bls_share(
0,
bls_secret_key.secret_key_share(0),
bls_secret_key.public_keys(),
),
]
}
pub fn gen_keys() -> Vec<PublicKey> {
gen_keypairs().iter().map(PublicKey::from).collect()
}
#[test]
fn debug_format() -> Result<()> {
use crate::Error;
if let Some(key) = gen_keys().first() {
let errored_response = QueryResponse::GetSequence(Err(Error::AccessDenied(*key)));
assert!(format!("{:?}", errored_response)
.contains("QueryResponse::GetSequence(AccessDenied(PublicKey::"));
Ok(())
} else {
Err(anyhow!("Could not generate public key"))
}
}
#[test]
fn try_from() -> Result<()> {
use QueryResponse::*;
let key = match gen_keys().first() {
Some(key) => *key,
None => return Err(anyhow!("Could not generate public key")),
};
let i_data = Blob::Public(PublicBlob::new(vec![1, 3, 1, 4]));
let e = Error::AccessDenied(key);
assert_eq!(
i_data,
GetBlob(Ok(i_data.clone()))
.try_into()
.map_err(|_| anyhow!("Mismatched types".to_string()))?
);
assert_eq!(
Err(TryFromError::Response(e.clone())),
Blob::try_from(GetBlob(Err(e.clone())))
);
let mut data = BTreeMap::new();
let _ = data.insert(vec![1], vec![10]);
let owners = PublicKey::Bls(threshold_crypto::SecretKey::random().public_key());
let m_data = Map::Unseq(UnseqMap::new_with_data(
*i_data.name(),
1,
data,
BTreeMap::new(),
owners,
));
assert_eq!(
m_data,
GetMap(Ok(m_data.clone()))
.try_into()
.map_err(|_| anyhow!("Mismatched types".to_string()))?
);
assert_eq!(
Err(TryFromError::Response(e.clone())),
Map::try_from(GetMap(Err(e)))
);
Ok(())
}
#[test]
fn serialization() -> Result<()> {
let keypair = &gen_keypairs()[0];
let pk = keypair.public_key();
let signature = keypair.sign(b"blabla");
let random_xor = XorName::random();
let id = MessageId(random_xor);
let message = Message::Query {
query: Query::Transfer(TransferQuery::GetBalance(pk)),
id,
};
let msg_envelope = MsgEnvelope {
message,
origin: MsgSender::client(pk, signature)?,
proxies: vec![],
};
let serialized = msg_envelope.serialize()?;
let deserialized = MsgEnvelope::from(serialized)?;
assert_eq!(deserialized, msg_envelope);
Ok(())
}
}