use super::helpers::{decode_ipc_msg, AuthResponseType};
use super::{
fetch::Range,
helpers::{xorname_from_pk, xorname_to_hex},
SafeApp,
};
use crate::{Error, Result};
use async_trait::async_trait;
use log::{debug, info, warn};
use safe_app::App;
use safe_core::{client::test_create_balance, immutable_data, Client, CoreError as SafeCoreError};
use safe_nd::{
ClientFullId, Coins, Error as SafeNdError, IDataAddress, MDataAction, MDataPermissionSet,
MDataSeqEntryActions, MDataSeqValue, PublicKey as SafeNdPublicKey, SDataAddress, SDataIndex,
SDataPrivUserPermissions, SDataPubUserPermissions, SDataUser, SeqMutableData, Transaction,
TransactionId, XorName,
};
use std::collections::BTreeMap;
pub use threshold_crypto::{PublicKey, SecretKey};
const APP_NOT_CONNECTED: &str = "Application is not connected to the network";
#[derive(Default)]
pub struct SafeAppScl {
safe_conn: Option<App>,
}
impl SafeAppScl {
fn get_safe_app(&self) -> Result<&App> {
match &self.safe_conn {
Some(app) => Ok(app),
None => Err(Error::ConnectionError(APP_NOT_CONNECTED.to_string())),
}
}
async fn mutate_seq_mdata_entries(
&self,
name: XorName,
tag: u64,
entry_actions: MDataSeqEntryActions,
error_msg: &str,
) -> Result<()> {
let client = &self.get_safe_app()?.client;
let message = error_msg.to_string();
client
.mutate_seq_mdata_entries(name, tag, entry_actions)
.await
.map_err(|err| {
if let SafeCoreError::DataError(SafeNdError::InvalidEntryActions(_)) = err {
Error::EntryExists(format!("{}: {}", message, err))
} else {
Error::NetDataError(format!("{}: {}", message, err))
}
})
}
}
#[async_trait]
impl SafeApp for SafeAppScl {
fn new() -> Self {
Self { safe_conn: None }
}
async fn connect(&mut self, app_id: &str, auth_credentials: Option<&str>) -> Result<()> {
debug!("Connecting to SAFE Network...");
let disconnect_cb = || {
warn!("Connection with the SAFE Network was lost");
};
let app = match auth_credentials {
Some(auth_credentials) => {
let auth_granted = decode_ipc_msg(auth_credentials)?;
match auth_granted {
AuthResponseType::Registered(authgranted) => {
App::registered(app_id.to_string(), authgranted, disconnect_cb).await
}
AuthResponseType::Unregistered(config) => {
App::unregistered(disconnect_cb, Some(config)).await
}
}
}
None => App::unregistered(disconnect_cb, None).await,
}
.map_err(|err| {
Error::ConnectionError(format!("Failed to connect to the SAFE Network: {:?}", err))
})?;
self.safe_conn = Some(app);
debug!("Successfully connected to the Network!!!");
Ok(())
}
async fn create_balance(
&mut self,
from_sk: Option<SecretKey>,
new_balance_owner: PublicKey,
amount: Coins,
) -> Result<XorName> {
let client = &self.get_safe_app()?.client;
let from_fullid = from_sk.map(ClientFullId::from);
client
.create_balance(
from_fullid.as_ref(),
SafeNdPublicKey::Bls(new_balance_owner),
amount,
None,
)
.await
.map_err(|err| {
if let SafeCoreError::DataError(SafeNdError::InsufficientBalance) = err {
Error::NotEnoughBalance(amount.to_string())
} else {
Error::NetDataError(format!("Failed to create a SafeKey: {:?}", err))
}
})?;
let xorname = xorname_from_pk(new_balance_owner);
Ok(xorname)
}
async fn allocate_test_coins(&mut self, owner_sk: SecretKey, amount: Coins) -> Result<XorName> {
info!("Creating test SafeKey with {} test coins", amount);
let xorname = xorname_from_pk(owner_sk.public_key());
test_create_balance(&ClientFullId::from(owner_sk), amount)
.await
.map_err(|e| Error::NetDataError(format!("Failed to allocate test coins: {:?}", e)))?;
Ok(xorname)
}
async fn get_balance_from_sk(&self, sk: SecretKey) -> Result<Coins> {
let client = &self.get_safe_app()?.client;
let coins = client
.get_balance(Some(&ClientFullId::from(sk)))
.await
.map_err(|e| Error::NetDataError(format!("Failed to retrieve balance: {:?}", e)))?;
Ok(coins)
}
async fn safecoin_transfer_to_xorname(
&mut self,
from_sk: Option<SecretKey>,
to_xorname: XorName,
tx_id: TransactionId,
amount: Coins,
) -> Result<Transaction> {
let client = &self.get_safe_app()?.client;
let from_fullid = from_sk.map(ClientFullId::from);
let tx = client
.transfer_coins(from_fullid.as_ref(), to_xorname, amount, Some(tx_id))
.await
.map_err(|err| match err {
SafeCoreError::DataError(SafeNdError::ExcessiveValue)
| SafeCoreError::DataError(SafeNdError::InsufficientBalance) => {
Error::NotEnoughBalance(amount.to_string())
}
SafeCoreError::DataError(SafeNdError::InvalidOperation) => {
Error::InvalidAmount(amount.to_string())
}
other => Error::NetDataError(format!("Failed to transfer coins: {:?}", other)),
})?;
Ok(tx)
}
async fn safecoin_transfer_to_pk(
&mut self,
from_sk: Option<SecretKey>,
to_pk: PublicKey,
tx_id: TransactionId,
amount: Coins,
) -> Result<Transaction> {
let to_xorname = xorname_from_pk(to_pk);
self.safecoin_transfer_to_xorname(from_sk, to_xorname, tx_id, amount)
.await
}
async fn put_public_immutable(&mut self, data: &[u8], dry_run: bool) -> Result<XorName> {
let client = &self.get_safe_app()?.client;
let data_vec = data.to_vec();
let data_map = if dry_run {
immutable_data::gen_data_map(
client, &data_vec, true, None,
)
.await
} else {
immutable_data::create(
client, &data_vec, true, None,
)
.await
}
.map_err(|e| {
Error::NetDataError(format!(
"Failed to create data map for Public ImmutableData: {:?}",
e
))
})?;
let xorname = *data_map.address().name();
if !dry_run {
client.put_idata(data_map).await.map_err(|e| {
Error::NetDataError(format!("Failed to PUT Public ImmutableData: {:?}", e))
})?;
}
Ok(xorname)
}
async fn get_public_immutable(&self, xorname: XorName, range: Range) -> Result<Vec<u8>> {
debug!("Fetching immutable data: {:?}", &xorname);
let client = &self.get_safe_app()?.client;
let immd_data_addr = IDataAddress::Pub(xorname);
let data = if let Some((start, end)) = range {
let len = if let Some(end_index) = end {
Some(end_index - start.unwrap_or_else(|| 0))
} else {
None
};
immutable_data::get_value(
client,
immd_data_addr,
start,
len,
None,
)
.await
} else {
immutable_data::get_value(
client,
immd_data_addr,
None,
None,
None,
)
.await
}
.map_err(|e| Error::NetDataError(format!("Failed to GET Public ImmutableData: {:?}", e)))?;
debug!(
"Public ImmutableData data successfully retrieved from: {:?}",
&xorname
);
Ok(data)
}
async fn put_mdata(
&mut self,
name: Option<XorName>,
tag: u64,
_permissions: Option<String>,
) -> Result<XorName> {
let safe_app = self.get_safe_app()?;
let client = &safe_app.client;
let owner_key_option = client.owner_key().await;
let owners = if let SafeNdPublicKey::Bls(owners) = owner_key_option {
owners
} else {
return Err(Error::Unexpected(
"Failed to retrieve public key.".to_string(),
));
};
let xorname = name.unwrap_or_else(rand::random);
let permission_set = MDataPermissionSet::new()
.allow(MDataAction::Read)
.allow(MDataAction::Insert)
.allow(MDataAction::Update)
.allow(MDataAction::Delete)
.allow(MDataAction::ManagePermissions);
let mut permission_map = BTreeMap::new();
let sign_pk = get_public_bls_key(safe_app).await?;
let app_pk = SafeNdPublicKey::Bls(sign_pk);
permission_map.insert(app_pk, permission_set);
let mdata = SeqMutableData::new_with_data(
xorname,
tag,
BTreeMap::new(),
permission_map,
SafeNdPublicKey::Bls(owners),
);
client
.put_seq_mutable_data(mdata)
.await
.map_err(|err| Error::NetDataError(format!("Failed to put mutable data: {}", err)))?;
Ok(xorname)
}
async fn get_mdata(&self, name: XorName, tag: u64) -> Result<SeqMutableData> {
let client = &self.get_safe_app()?.client;
client
.get_seq_mdata(name, tag)
.await
.map_err(|e| Error::NetDataError(format!("Failed to get MD: {:?}", e)))
}
async fn mdata_insert(
&mut self,
name: XorName,
tag: u64,
key: &[u8],
value: &[u8],
) -> Result<()> {
let entry_actions = MDataSeqEntryActions::new();
let entry_actions = entry_actions.ins(key.to_vec(), value.to_vec(), 0);
self.mutate_seq_mdata_entries(name, tag, entry_actions, "Failed to insert to SeqMD")
.await
}
async fn mdata_get_value(&self, name: XorName, tag: u64, key: &[u8]) -> Result<MDataSeqValue> {
let client = &self.get_safe_app()?.client;
let key_vec = key.to_vec();
client
.get_seq_mdata_value(name, tag, key_vec)
.await
.map_err(|err| match err {
SafeCoreError::DataError(SafeNdError::AccessDenied) => {
Error::AccessDenied(format!("Failed to retrieve a key: {:?}", key))
}
SafeCoreError::DataError(SafeNdError::NoSuchData) => {
Error::ContentNotFound(format!(
"Sequenced MutableData not found at Xor name: {}",
xorname_to_hex(&name)
))
}
SafeCoreError::DataError(SafeNdError::NoSuchEntry) => {
Error::EntryNotFound(format!(
"Entry not found in Sequenced MutableData found at Xor name: {}",
xorname_to_hex(&name)
))
}
err => Error::NetDataError(format!("Failed to retrieve a key. {:?}", err)),
})
}
async fn mdata_list_entries(
&self,
name: XorName,
tag: u64,
) -> Result<BTreeMap<Vec<u8>, MDataSeqValue>> {
let client = &self.get_safe_app()?.client;
client
.list_seq_mdata_entries(name, tag)
.await
.map_err(|err| match err {
SafeCoreError::DataError(SafeNdError::AccessDenied) => {
Error::AccessDenied(format!(
"Failed to get Sequenced MutableData at: {:?} (type tag: {})",
name, tag
))
}
SafeCoreError::DataError(SafeNdError::NoSuchData) => {
Error::ContentNotFound(format!(
"Sequenced MutableData not found at Xor name: {} (type tag: {})",
xorname_to_hex(&name),
tag
))
}
SafeCoreError::DataError(SafeNdError::NoSuchEntry) => {
Error::EntryNotFound(format!(
"Entry not found in Sequenced MutableData found at Xor name: {} (type tag: {})",
xorname_to_hex(&name),
tag
))
}
err => {
Error::NetDataError(format!("Failed to get Sequenced MutableData. {:?}", err))
}
})
}
async fn mdata_update(
&mut self,
name: XorName,
tag: u64,
key: &[u8],
value: &[u8],
version: u64,
) -> Result<()> {
let entry_actions = MDataSeqEntryActions::new();
let entry_actions = entry_actions.update(key.to_vec(), value.to_vec(), version);
self.mutate_seq_mdata_entries(name, tag, entry_actions, "Failed to update SeqMD")
.await
}
async fn store_sequence_data(
&mut self,
data: &[u8],
name: Option<XorName>,
tag: u64,
_permissions: Option<String>,
private: bool,
) -> Result<XorName> {
debug!(
"Storing {} Sequence data with tag type: {:?}, xorname: {:?}",
if private { "Private" } else { "Public" },
tag,
name
);
let safe_app: &App = self.get_safe_app()?;
let xorname = name.unwrap_or_else(rand::random);
info!("Xorname for storage: {:?}", &xorname);
let app_public_key = get_public_bls_key(safe_app).await?;
let user_acc_owner = safe_app.client.owner_key().await;
let address = if private {
let mut perms = BTreeMap::default();
let _ = perms.insert(
SafeNdPublicKey::Bls(app_public_key),
SDataPrivUserPermissions::new(true, true, true),
);
safe_app
.client
.store_priv_sdata(xorname, tag, user_acc_owner, perms)
.await
.map_err(|e| {
Error::NetDataError(format!("Failed to store Private Sequence data: {:?}", e))
})?
} else {
let user_app = SDataUser::Key(SafeNdPublicKey::Bls(app_public_key));
let mut perms = BTreeMap::default();
let _ = perms.insert(user_app, SDataPubUserPermissions::new(true, true));
safe_app
.client
.store_pub_sdata(xorname, tag, user_acc_owner, perms)
.await
.map_err(|e| {
Error::NetDataError(format!("Failed to store Public Sequence data: {:?}", e))
})?
};
let _op = safe_app
.client
.sdata_append(address, data.to_vec())
.await
.map_err(|e| {
Error::NetDataError(format!("Failed to append data to the Sequence: {:?}", e))
})?;
Ok(xorname)
}
async fn sequence_get_last_entry(
&self,
name: XorName,
tag: u64,
private: bool,
) -> Result<(u64, Vec<u8>)> {
debug!(
"Fetching {} Sequence data w/ type: {:?}, xorname: {:?}",
if private { "Private" } else { "Public" },
tag,
name
);
let safe_app: &App = self.get_safe_app()?;
let sequence_address = if private {
SDataAddress::Private { name, tag }
} else {
SDataAddress::Public { name, tag }
};
safe_app
.client
.get_sdata_last_entry(sequence_address)
.await
.map_err(|err| {
if let SafeCoreError::DataError(SafeNdError::NoSuchEntry) = err {
Error::EmptyContent(format!("Empty Sequence found at XoR name {}", name))
} else {
Error::NetDataError(format!(
"Failed to retrieve last entry from Sequence data: {:?}",
err
))
}
})
}
async fn sequence_get_entry(
&self,
name: XorName,
tag: u64,
index: u64,
private: bool,
) -> Result<Vec<u8>> {
debug!(
"Fetching {} Sequence data w/ type: {:?}, xorname: {:?}",
if private { "Private" } else { "Public" },
tag,
name
);
let safe_app: &App = self.get_safe_app()?;
let sequence_address = if private {
SDataAddress::Private { name, tag }
} else {
SDataAddress::Public { name, tag }
};
let start = SDataIndex::FromStart(index);
let end = SDataIndex::FromStart(index + 1);
let res = safe_app
.client
.get_sdata_range(sequence_address, (start, end))
.await
.map_err(|err| {
if let SafeCoreError::DataError(SafeNdError::NoSuchEntry) = err {
Error::VersionNotFound(format!(
"Invalid version ({}) for Sequence found at XoR name {}",
index, name
))
} else {
Error::NetDataError(format!(
"Failed to retrieve entry at index {} from Sequence data: {:?}",
index, err
))
}
})?;
let entry = res.get(0).ok_or_else(|| {
Error::EmptyContent(format!(
"Empty Sequence found at Xor name {}",
xorname_to_hex(&name)
))
})?;
Ok(entry.to_vec())
}
async fn sequence_append(
&mut self,
data: &[u8],
name: XorName,
tag: u64,
private: bool,
) -> Result<()> {
debug!(
"Appending to {} Sequence data w/ type: {:?}, xorname: {:?}",
if private { "Private" } else { "Public" },
tag,
name
);
let safe_app: &App = self.get_safe_app()?;
let sequence_address = if private {
SDataAddress::Private { name, tag }
} else {
SDataAddress::Public { name, tag }
};
safe_app
.client
.sdata_append(sequence_address, data.to_vec())
.await
.map_err(|e| Error::NetDataError(format!("Failed to append to Sequence: {:?}", e)))
}
}
async fn get_public_bls_key(safe_app: &App) -> Result<PublicKey> {
let pk = safe_app
.client
.public_key()
.await
.bls()
.ok_or_else(|| Error::Unexpected("Client's key is not a BLS Public Key".to_string()))?;
Ok(pk)
}