-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add new Wallet trait, implement for indy wallet #1084
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
use serde::{Deserialize, Serialize}; | ||
|
||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] | ||
pub enum EntryTag { | ||
Encrypted(String, String), | ||
Plaintext(String, String), | ||
} | ||
|
||
#[derive(Debug, Default, Clone, PartialEq)] | ||
pub struct EntryTags { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder what is the purpose of this struct. Why encapsulate the vec and reimplement a subset of vec functionality for it, instead of using the vec itself? I assume it is something like we want to keep open the possibility of changing the Vec for e.g. a HashSet? If so, why can't the decision of using a Vec vs HashSet be made now, what are the unknowns? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The underlying implementation is irrelevant. I needed a type that would represent a collection of tags which would be independent on any implementation as vdrtools wallet uses |
||
inner: Vec<EntryTag>, | ||
} | ||
|
||
impl EntryTags { | ||
pub fn new(inner: Vec<EntryTag>) -> Self { | ||
Self { inner } | ||
} | ||
|
||
pub fn add(&mut self, tag: EntryTag) { | ||
self.inner.push(tag) | ||
} | ||
|
||
pub fn is_empty(&self) -> bool { | ||
self.inner.is_empty() | ||
} | ||
} | ||
|
||
impl IntoIterator for EntryTags { | ||
type Item = EntryTag; | ||
|
||
type IntoIter = std::vec::IntoIter<Self::Item>; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
self.inner.into_iter() | ||
} | ||
} | ||
|
||
impl FromIterator<EntryTag> for EntryTags { | ||
fn from_iter<T: IntoIterator<Item = EntryTag>>(iter: T) -> Self { | ||
let mut tags = Self::default(); | ||
|
||
for item in iter { | ||
tags.add(item); | ||
} | ||
tags | ||
} | ||
} | ||
|
||
impl From<Vec<EntryTag>> for EntryTags { | ||
fn from(value: Vec<EntryTag>) -> Self { | ||
value.into_iter().fold(Self::default(), |mut memo, item| { | ||
memo.add(item); | ||
memo | ||
}) | ||
} | ||
} | ||
|
||
impl From<EntryTags> for Vec<EntryTag> { | ||
fn from(value: EntryTags) -> Self { | ||
value.inner | ||
} | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably the most important comment within this review: You placed the tests into indy implementations directly, but the tests themselves are not necessarily indy specific - they rely on the The current approach would indicate that given you have test So in my view, solving that boils down to:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good, there is one small complication though. I created a custom setup for now, any thoughts on this are appreciated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As agreed to, we will look into removing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tracking issue #1101 . |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use async_trait::async_trait; | ||
use public_key::KeyType; | ||
use vdrtools::{DidMethod, DidValue, KeyInfo, Locator, MyDidInfo}; | ||
|
||
use crate::{ | ||
errors::error::{AriesVcxCoreError, AriesVcxCoreErrorKind, VcxCoreResult}, | ||
wallet::{indy::IndySdkWallet, structs_io::UnpackMessageOutput}, | ||
wallet2::{DidData, DidWallet, Key}, | ||
}; | ||
|
||
#[async_trait] | ||
impl DidWallet for IndySdkWallet { | ||
async fn create_and_store_my_did( | ||
&self, | ||
seed: Option<&str>, | ||
method_name: Option<&str>, | ||
) -> VcxCoreResult<DidData> { | ||
let res = Locator::instance() | ||
.did_controller | ||
.create_and_store_my_did( | ||
self.wallet_handle, | ||
MyDidInfo { | ||
method_name: method_name.map(|m| DidMethod(m.into())), | ||
seed: seed.map(Into::into), | ||
..MyDidInfo::default() | ||
}, | ||
) | ||
.await?; | ||
|
||
Ok(DidData { | ||
did: res.0, | ||
verkey: Key::from_base58(&res.1, KeyType::Ed25519).map_err(|err| { | ||
AriesVcxCoreError::from_msg(AriesVcxCoreErrorKind::WalletError, err) | ||
})?, | ||
}) | ||
} | ||
|
||
async fn did_key(&self, did: &str) -> VcxCoreResult<Key> { | ||
let res = Locator::instance() | ||
.did_controller | ||
.key_for_local_did(self.wallet_handle, DidValue(did.into())) | ||
.await?; | ||
|
||
Key::from_base58(&res, KeyType::Ed25519) | ||
.map_err(|err| AriesVcxCoreError::from_msg(AriesVcxCoreErrorKind::WalletError, err)) | ||
} | ||
|
||
async fn replace_did_key_start(&self, did: &str, seed: Option<&str>) -> VcxCoreResult<Key> { | ||
let key_info = KeyInfo { | ||
seed: seed.map(Into::into), | ||
..Default::default() | ||
}; | ||
|
||
let key_string = Locator::instance() | ||
.did_controller | ||
.replace_keys_start(self.wallet_handle, key_info, DidValue(did.into())) | ||
.await?; | ||
|
||
let key = Key::from_base58(&key_string, KeyType::Ed25519) | ||
.map_err(|err| AriesVcxCoreError::from_msg(AriesVcxCoreErrorKind::WalletError, err))?; | ||
|
||
Ok(key) | ||
} | ||
|
||
async fn replace_did_key_apply(&self, did: &str) -> VcxCoreResult<()> { | ||
Ok(Locator::instance() | ||
.did_controller | ||
.replace_keys_apply(self.wallet_handle, DidValue(did.into())) | ||
.await?) | ||
} | ||
|
||
async fn sign(&self, key: &Key, msg: &[u8]) -> VcxCoreResult<Vec<u8>> { | ||
Locator::instance() | ||
.crypto_controller | ||
.crypto_sign(self.wallet_handle, &key.base58(), msg) | ||
.await | ||
.map_err(From::from) | ||
} | ||
|
||
async fn verify(&self, key: &Key, msg: &[u8], signature: &[u8]) -> VcxCoreResult<bool> { | ||
Locator::instance() | ||
.crypto_controller | ||
.crypto_verify(&key.base58(), msg, signature) | ||
.await | ||
.map_err(From::from) | ||
} | ||
|
||
async fn pack_message( | ||
&self, | ||
sender_vk: Option<Key>, | ||
receiver_keys: Vec<Key>, | ||
msg: &[u8], | ||
) -> VcxCoreResult<Vec<u8>> { | ||
let receiver_keys_str = receiver_keys.into_iter().map(|key| key.base58()).collect(); | ||
|
||
Ok(Locator::instance() | ||
.crypto_controller | ||
.pack_msg( | ||
msg.into(), | ||
receiver_keys_str, | ||
sender_vk.map(|key| key.base58()), | ||
self.wallet_handle, | ||
) | ||
.await?) | ||
} | ||
|
||
async fn unpack_message(&self, msg: &[u8]) -> VcxCoreResult<UnpackMessageOutput> { | ||
let unpacked_bytes = Locator::instance() | ||
.crypto_controller | ||
.unpack_msg(serde_json::from_slice(msg)?, self.wallet_handle) | ||
.await?; | ||
|
||
let res: UnpackMessageOutput = | ||
serde_json::from_slice(&unpacked_bytes[..]).map_err(|err| { | ||
AriesVcxCoreError::from_msg(AriesVcxCoreErrorKind::ParsingError, err.to_string()) | ||
})?; | ||
|
||
Ok(res) | ||
} | ||
} |
Patrik-Stas marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
use async_trait::async_trait; | ||
use indy_api_types::domain::wallet::Record as IndyRecord; | ||
use serde::Deserialize; | ||
use serde_json::Value; | ||
use vdrtools::Locator; | ||
|
||
use super::{SEARCH_OPTIONS, WALLET_OPTIONS}; | ||
use crate::{ | ||
errors::error::{AriesVcxCoreError, VcxCoreResult}, | ||
wallet::indy::IndySdkWallet, | ||
wallet2::{entry_tag::EntryTags, Record, RecordWallet, SearchFilter}, | ||
}; | ||
|
||
#[async_trait] | ||
impl RecordWallet for IndySdkWallet { | ||
mirgee marked this conversation as resolved.
Show resolved
Hide resolved
|
||
async fn add_record(&self, record: Record) -> VcxCoreResult<()> { | ||
let tags_map = if record.tags.is_empty() { | ||
None | ||
} else { | ||
Some(record.tags.into()) | ||
}; | ||
|
||
Ok(Locator::instance() | ||
.non_secret_controller | ||
.add_record( | ||
self.wallet_handle, | ||
record.category, | ||
record.name, | ||
record.value, | ||
tags_map, | ||
) | ||
.await?) | ||
} | ||
|
||
async fn get_record(&self, name: &str, category: &str) -> VcxCoreResult<Record> { | ||
let res = Locator::instance() | ||
.non_secret_controller | ||
.get_record( | ||
self.wallet_handle, | ||
category.into(), | ||
name.into(), | ||
WALLET_OPTIONS.into(), | ||
) | ||
.await?; | ||
|
||
let indy_record: IndyRecord = serde_json::from_str(&res)?; | ||
|
||
Ok(indy_record.into()) | ||
} | ||
|
||
async fn update_record_tags( | ||
&self, | ||
name: &str, | ||
category: &str, | ||
new_tags: EntryTags, | ||
) -> VcxCoreResult<()> { | ||
Ok(Locator::instance() | ||
.non_secret_controller | ||
.update_record_tags( | ||
self.wallet_handle, | ||
category.into(), | ||
name.into(), | ||
new_tags.into(), | ||
) | ||
.await?) | ||
} | ||
|
||
async fn update_record_value( | ||
&self, | ||
name: &str, | ||
category: &str, | ||
new_value: &str, | ||
) -> VcxCoreResult<()> { | ||
Ok(Locator::instance() | ||
.non_secret_controller | ||
.update_record_value( | ||
self.wallet_handle, | ||
category.into(), | ||
name.into(), | ||
new_value.into(), | ||
) | ||
.await?) | ||
} | ||
|
||
async fn delete_record(&self, name: &str, category: &str) -> VcxCoreResult<()> { | ||
Ok(Locator::instance() | ||
.non_secret_controller | ||
.delete_record(self.wallet_handle, category.into(), name.into()) | ||
.await?) | ||
} | ||
|
||
async fn search_record( | ||
&self, | ||
category: &str, | ||
search_filter: Option<SearchFilter>, | ||
) -> VcxCoreResult<Vec<Record>> { | ||
let json_filter = search_filter | ||
.map(|filter| match filter { | ||
SearchFilter::JsonFilter(inner) => Ok::<String, AriesVcxCoreError>(inner), | ||
}) | ||
.transpose()?; | ||
|
||
let query_json = json_filter.unwrap_or("{}".into()); | ||
|
||
let search_handle = Locator::instance() | ||
.non_secret_controller | ||
.open_search( | ||
self.wallet_handle, | ||
category.into(), | ||
query_json, | ||
SEARCH_OPTIONS.into(), | ||
) | ||
.await?; | ||
|
||
let next = || async { | ||
let record = Locator::instance() | ||
.non_secret_controller | ||
.fetch_search_next_records(self.wallet_handle, search_handle, 1) | ||
.await?; | ||
|
||
let indy_res: Value = serde_json::from_str(&record)?; | ||
|
||
indy_res | ||
.get("records") | ||
.and_then(|v| v.as_array()) | ||
.and_then(|arr| arr.first()) | ||
.map(|item| IndyRecord::deserialize(item).map_err(AriesVcxCoreError::from)) | ||
.transpose() | ||
}; | ||
|
||
let mut records = Vec::new(); | ||
while let Some(record) = next().await? { | ||
records.push(record.into()); | ||
} | ||
|
||
Ok(records) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This enum is a little confusing - what is the purpose of an encrypted tag? Why is it a key-value pair instead of a value? How is it always possible to, given a string, infer whether it is "encrypted" or not (whatever that means)? Why would the wallet return record with encrypted tag, shouldn't they all be decrypted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All good questions, I am afraid I do not have answers to all of them.
For some reason, the tags are key-value pairs in both vdrtools wallet and askar. Determining whether the tag value is encrypted is a responsibility of the wallet implementation.