Skip to content

Commit

Permalink
Metadata for domains
Browse files Browse the repository at this point in the history
Signed-off-by: Alexey Kalita <kalita.alexey@outlook.com>
  • Loading branch information
KalitaAlexey committed Oct 20, 2021
1 parent 066515f commit c9a4fa8
Show file tree
Hide file tree
Showing 13 changed files with 276 additions and 58 deletions.
3 changes: 2 additions & 1 deletion client/tests/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"Domain": {
"name": "wonderland",
"accounts": {},
"asset_definitions": {}
"asset_definitions": {},
"metadata": {}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions core/benches/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ fn validate_blocks(criterion: &mut Criterion) {
name: domain_name.clone(),
accounts,
asset_definitions,
metadata: Metadata::new(),
};
let mut domains = BTreeMap::new();
domains.insert(domain_name, domain);
Expand Down
59 changes: 59 additions & 0 deletions core/src/smartcontracts/isi/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::prelude::*;
/// ISI module contains all instructions related to domains:
/// - creating/changing assets
/// - registering/unregistering accounts
/// - update metadata
/// - transfer, etc.
pub mod isi {
use super::*;
Expand Down Expand Up @@ -156,6 +157,48 @@ pub mod isi {
Ok(())
}
}

impl<W: WorldTrait> Execute<W> for SetKeyValue<Domain, String, Value> {
type Error = Error;

fn execute(
self,
_authority: <Account as Identifiable>::Id,
wsv: &WorldStateView<W>,
) -> Result<(), Error> {
let Self {
object_id,
key,
value,
} = self;
let limits = wsv.config.domain_metadata_limits;
wsv.modify_domain(&object_id, |domain| {
domain.metadata.insert_with_limits(key, value, limits)?;
Ok(())
})?;
Ok(())
}
}

impl<W: WorldTrait> Execute<W> for RemoveKeyValue<Domain, String> {
type Error = Error;

fn execute(
self,
_authority: <Account as Identifiable>::Id,
wsv: &WorldStateView<W>,
) -> Result<(), Error> {
let Self { object_id, key } = self;
wsv.modify_domain(&object_id, |domain| {
domain
.metadata
.remove(&key)
.ok_or(FindError::MetadataKey(key))?;
Ok(())
})?;
Ok(())
}
}
}

/// Query module provides [`Query`] Domain related implementations.
Expand Down Expand Up @@ -186,6 +229,22 @@ pub mod query {
}
}

impl<W: WorldTrait> Query<W> for FindDomainKeyValueByIdAndKey {
#[log]
fn execute(&self, wsv: &WorldStateView<W>) -> Result<Self::Output> {
let name = self
.name
.evaluate(wsv, &Context::default())
.wrap_err("Failed to get domain name")?;
let key = self
.key
.evaluate(wsv, &Context::default())
.wrap_err("Failed to get key")?;
wsv.map_domain(&name, |domain| domain.metadata.get(&key).map(Clone::clone))?
.ok_or_else(|| eyre!("No metadata entry with this key."))
}
}

impl<W: WorldTrait> Query<W> for FindAssetDefinitionKeyValueByIdAndKey {
fn execute(&self, wsv: &WorldStateView<W>) -> Result<Self::Output> {
let id = self
Expand Down
26 changes: 26 additions & 0 deletions core/src/smartcontracts/isi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,9 @@ impl<W: WorldTrait> Execute<W> for SetKeyValueBox {
SetKeyValue::<Account, String, Value>::new(account_id, key, value)
.execute(authority, wsv)
}
IdBox::DomainName(name) => {
SetKeyValue::<Domain, String, Value>::new(name, key, value).execute(authority, wsv)
}
_ => Err(eyre!("Unsupported set key-value instruction.").into()),
}
}
Expand Down Expand Up @@ -558,4 +561,27 @@ mod tests {
);
Ok(())
}

#[test]
fn domain_metadata() -> Result<()> {
let wsv = WorldStateView::new(world_with_test_domains()?);
let domain_name = "wonderland".to_owned();
let account_id = AccountId::new("alice", "wonderland");
SetKeyValueBox::new(
IdBox::from(domain_name.clone()),
"Bytes".to_owned(),
vec![1_u32, 2_u32, 3_u32],
)
.execute(account_id, &wsv)?;
let bytes = wsv.domain(&domain_name)?.metadata.get("Bytes").cloned();
assert_eq!(
bytes,
Some(Value::Vec(vec![
Value::U32(1),
Value::U32(2),
Value::U32(3)
]))
);
Ok(())
}
}
22 changes: 22 additions & 0 deletions core/src/smartcontracts/isi/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ impl<W: WorldTrait> Query<W> for QueryBox {
FindAssetQuantityById(query) => query.execute_into_value(wsv),
FindAllDomains(query) => query.execute_into_value(wsv),
FindDomainByName(query) => query.execute_into_value(wsv),
FindDomainKeyValueByIdAndKey(query) => query.execute_into_value(wsv),
FindAllPeers(query) => query.execute_into_value(wsv),
FindAssetKeyValueByIdAndKey(query) => query.execute_into_value(wsv),
FindAccountKeyValueByIdAndKey(query) => query.execute_into_value(wsv),
Expand Down Expand Up @@ -267,4 +268,25 @@ mod tests {
);
Ok(())
}

#[test]
fn domain_metadata() -> Result<()> {
let wsv = WorldStateView::new(world_with_test_domains()?);
let domain_name = "wonderland".to_owned();
let key = "Bytes".to_owned();
wsv.modify_domain(&domain_name, |domain| {
domain.metadata.insert_with_limits(
key.clone(),
Value::Vec(vec![Value::U32(1), Value::U32(2), Value::U32(3)]),
MetadataLimits::new(10, 100),
)?;
Ok(())
})?;
let bytes = FindDomainKeyValueByIdAndKey::new(domain_name, key).execute(&wsv)?;
assert_eq!(
bytes,
Value::Vec(vec![Value::U32(1), Value::U32(2), Value::U32(3)])
);
Ok(())
}
}
27 changes: 27 additions & 0 deletions core/src/wsv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,30 @@ impl<W: WorldTrait> WorldStateView<W> {
Ok(domain)
}

/// Get `Domain` and pass it to closure.
/// # Errors
/// Fails if there is no domain or account
pub fn map_domain<T>(
&self,
id: &<Domain as Identifiable>::Id,
f: impl FnOnce(&Domain) -> T,
) -> Result<T> {
let domain = self.domain(id)?;
Ok(f(domain.value()))
}

/// Get `Domain` and pass it to closure to modify it
/// # Errors
/// Fails if there is no domain
pub fn modify_domain(
&self,
name: &str,
f: impl FnOnce(&mut Domain) -> Result<()>,
) -> Result<()> {
let mut domain = self.domain_mut(name)?;
f(domain.value_mut())
}

/// Get `Account` and pass it to closure.
/// # Errors
/// Fails if there is no domain or account
Expand Down Expand Up @@ -466,6 +490,8 @@ pub mod config {
pub asset_definition_metadata_limits: MetadataLimits,
/// [`MetadataLimits`] of any account's metadata.
pub account_metadata_limits: MetadataLimits,
/// [`MetadataLimits`] of any domain's metadata.
pub domain_metadata_limits: MetadataLimits,
/// [`LengthLimits`]for the number of chars in identifiers that can be stored in the WSV.
pub ident_length_limits: LengthLimits,
}
Expand All @@ -476,6 +502,7 @@ pub mod config {
asset_metadata_limits: DEFAULT_METADATA_LIMITS,
asset_definition_metadata_limits: DEFAULT_METADATA_LIMITS,
account_metadata_limits: DEFAULT_METADATA_LIMITS,
domain_metadata_limits: DEFAULT_METADATA_LIMITS,
ident_length_limits: DEFAULT_IDENT_LENGTH_LIMITS,
}
}
Expand Down
3 changes: 2 additions & 1 deletion core/test_network/tests/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"Domain": {
"name": "wonderland",
"accounts": {},
"asset_definitions": {}
"asset_definitions": {},
"metadata": {}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion core/tests/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"Domain": {
"name": "wonderland",
"accounts": {},
"asset_definitions": {}
"asset_definitions": {},
"metadata": {}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions data_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1737,6 +1737,7 @@ pub mod domain {
use crate::{
account::{Account, AccountsMap, GenesisAccount},
asset::AssetDefinitionsMap,
metadata::Metadata,
Identifiable, Name, Value,
};

Expand Down Expand Up @@ -1770,6 +1771,7 @@ pub mod domain {
))
.collect(),
asset_definitions: BTreeMap::default(),
metadata: Metadata::new(),
}
}
}
Expand All @@ -1785,6 +1787,8 @@ pub mod domain {
pub accounts: AccountsMap,
/// Assets of the domain.
pub asset_definitions: AssetDefinitionsMap,
/// Metadata of this domain as a key-value store.
pub metadata: Metadata,
}

impl FromStr for Domain {
Expand Down Expand Up @@ -1813,6 +1817,7 @@ pub mod domain {
name: name.to_owned(),
accounts: AccountsMap::new(),
asset_definitions: AssetDefinitionsMap::new(),
metadata: Metadata::new(),
}
}

Expand Down Expand Up @@ -1843,6 +1848,7 @@ pub mod domain {
name: name.to_owned(),
accounts: accounts_map,
asset_definitions: AssetDefinitionsMap::new(),
metadata: Metadata::new(),
}
}
}
Expand Down
43 changes: 42 additions & 1 deletion data_model/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ pub enum QueryBox {
FindAllDomains(FindAllDomains),
/// `FindDomainByName` variant.
FindDomainByName(FindDomainByName),
/// `FindDomainKeyValueByIdAndKey` variant.
FindDomainKeyValueByIdAndKey(FindDomainKeyValueByIdAndKey),
/// `FindAllPeers` variant.
FindAllPeers(FindAllPeers),
/// `FindTransactionsByAccountId` variant.
Expand Down Expand Up @@ -951,9 +953,48 @@ pub mod domain {
}
}

/// `FindDomainKeyValueByIdAndKey` Iroha Query will find a [`Value`] of the key-value metadata pair
/// in the specified domain.
#[derive(
Clone,
Debug,
Io,
Serialize,
Deserialize,
Encode,
Decode,
PartialEq,
Eq,
PartialOrd,
Ord,
IntoSchema,
)]
pub struct FindDomainKeyValueByIdAndKey {
/// `Name` of an domain to find.
pub name: EvaluatesTo<Name>,
/// Key of the specific key-value in the domain's metadata.
pub key: EvaluatesTo<String>,
}

impl FindDomainKeyValueByIdAndKey {
/// Default `FindDomainKeyValueByIdAndKey` constructor.
pub fn new(
name: impl Into<EvaluatesTo<Name>>,
key: impl Into<EvaluatesTo<String>>,
) -> Self {
let name = name.into();
let key = key.into();
FindDomainKeyValueByIdAndKey { name, key }
}
}

impl QueryOutput for FindDomainKeyValueByIdAndKey {
type Output = Value;
}

/// The prelude re-exports most commonly used traits, structs and macros from this crate.
pub mod prelude {
pub use super::{FindAllDomains, FindDomainByName};
pub use super::{FindAllDomains, FindDomainByName, FindDomainKeyValueByIdAndKey};
}
}

Expand Down
21 changes: 21 additions & 0 deletions docs/source/references/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ Configuration of iroha is done via options in the following document. Here is de
"max_len": 1048576,
"max_entry_byte_size": 4096
},
"DOMAIN_METADATA_LIMITS": {
"max_len": 1048576,
"max_entry_byte_size": 4096
},
"IDENT_LENGTH_LIMITS": {
"min": 1,
"max": 128
Expand Down Expand Up @@ -609,6 +613,10 @@ Has type `WorldStateViewConfiguration`. Can be configured via environment variab
"max_entry_byte_size": 4096,
"max_len": 1048576
},
"DOMAIN_METADATA_LIMITS": {
"max_entry_byte_size": 4096,
"max_len": 1048576
},
"IDENT_LENGTH_LIMITS": {
"max": 128,
"min": 1
Expand Down Expand Up @@ -655,6 +663,19 @@ Has type `MetadataLimits`. Can be configured via environment variable `WSV_ASSET
}
```

### `wsv_configuration.domain_metadata_limits`

[`MetadataLimits`] of any domain's metadata.

Has type `MetadataLimits`. Can be configured via environment variable `WSV_DOMAIN_METADATA_LIMITS`

```json
{
"max_entry_byte_size": 4096,
"max_len": 1048576
}
```

### `wsv_configuration.ident_length_limits`

[`LengthLimits`]for the number of chars in identifiers that can be stored in the WSV.
Expand Down
3 changes: 2 additions & 1 deletion dsl/tests/genesis.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"Domain": {
"name": "wonderland",
"accounts": {},
"asset_definitions": {}
"asset_definitions": {},
"metadata": {}
}
}
}
Expand Down
Loading

0 comments on commit c9a4fa8

Please sign in to comment.