mod account;
mod auth;
mod blob;
mod cmd;
mod data;
mod duty;
mod map;
mod network;
mod query;
mod sender;
mod sequence;
mod transfer;
pub use self::{
account::{Account, AccountRead, AccountWrite, MAX_LOGIN_PACKET_BYTES},
auth::{AuthCmd, AuthQuery},
blob::{BlobRead, BlobWrite},
cmd::Cmd,
data::{DataCmd, DataQuery},
duty::{AdultDuties, Duty, ElderDuties, NodeDuties},
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, utils, AppPermissions, Blob, Error, Map, MapEntries, MapPermissionSet,
MapValue, MapValues, Money, PublicKey, ReplicaEvent, ReplicaPublicKeySet, Result, Sequence,
SequenceEntries, SequenceEntry, SequencePermissions, SequencePrivatePolicy,
SequencePublicPolicy, Signature, TransferAgreementProof, TransferValidated,
};
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeMap, BTreeSet},
convert::TryFrom,
fmt,
};
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() {
utils::serialise(&self.message)?
} else {
let mut msg = self.clone();
let _ = msg.proxies.pop();
utils::serialise(&msg)?
};
let sender = self.most_recent_sender();
Ok(sender.verify(&data))
}
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 {
Auth(_) => Ok(Section(self.origin.address().xorname())),
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::Payment))
| 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::Payment))
| 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,
}
}
}
#[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()
}
}
#[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<Vec<ReplicaEvent>>),
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!(Vec<ReplicaEvent>, 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 crate::keys::tests::gen_keys;
use crate::{PublicBlob, UnseqMap};
use anyhow::{anyhow, Result};
use std::convert::{TryFrom, TryInto};
#[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(())
}
}