Skip to content

Commit

Permalink
[feature] #4137: for store tranfer instruction (#4258)
Browse files Browse the repository at this point in the history
 *[feature]: add store asset transfer instruction
---------

Signed-off-by: Asem Abdelhady <[email protected]>
  • Loading branch information
Asem-Abdelhady authored Feb 13, 2024
1 parent 7501abe commit e3a4f42
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 16 deletions.
88 changes: 76 additions & 12 deletions client/tests/integration/transfer_asset.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
use std::str::FromStr;

use iroha_client::{
client::{self, QueryResult},
crypto::KeyPair,
data_model::{isi::Instruction, prelude::*, Registered},
};
use iroha_data_model::{
account::{Account, AccountId},
asset::{Asset, AssetDefinition},
isi::InstructionBox,
name::Name,
};
use iroha_primitives::fixed::Fixed;
use test_network::*;

Expand Down Expand Up @@ -33,8 +41,8 @@ fn simulate_transfer_big_quantity() {
#[test]
fn simulate_transfer_fixed() {
simulate_transfer(
Fixed::try_from(200_f64).expect("Valid"),
&Fixed::try_from(20_f64).expect("Valid"),
Fixed::try_from(200_f64).unwrap(),
&Fixed::try_from(20_f64).unwrap(),
AssetDefinition::fixed,
Mint::asset_fixed,
Transfer::asset_fixed,
Expand All @@ -47,15 +55,62 @@ fn simulate_transfer_fixed() {
#[should_panic(expected = "insufficient funds")]
fn simulate_insufficient_funds() {
simulate_transfer(
Fixed::try_from(20_f64).expect("Valid"),
&Fixed::try_from(200_f64).expect("Valid"),
Fixed::try_from(20_f64).unwrap(),
&Fixed::try_from(200_f64).unwrap(),
AssetDefinition::fixed,
Mint::asset_fixed,
Transfer::asset_fixed,
10_800,
)
}

#[test]
fn simulate_transfer_store_asset() {
let (_rt, _peer, iroha_client) = <PeerBuilder>::new().with_port(10_805).start_with_runtime();
wait_for_genesis_committed(&[iroha_client.clone()], 0);
let (alice_id, mouse_id) = generate_two_ids();
let create_mouse = create_mouse(mouse_id.clone());
let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().unwrap();
let create_asset =
Register::asset_definition(AssetDefinition::store(asset_definition_id.clone()));
let set_key_value = SetKeyValue::asset(
AssetId::new(asset_definition_id.clone(), alice_id.clone()),
Name::from_str("alicek").unwrap(),
true,
);

let instructions: [InstructionBox; 3] = [
// create_alice.into(), We don't need to register Alice, because she is created in genesis
create_mouse.into(),
create_asset.into(),
set_key_value.into(),
];

iroha_client
.submit_all_blocking(instructions)
.expect("Failed to prepare state.");

let transfer_asset = Transfer::asset_store(
AssetId::new(asset_definition_id.clone(), alice_id.clone()),
mouse_id.clone(),
);
let transfer_box: InstructionBox = transfer_asset.into();

iroha_client
.submit_till(
transfer_box,
client::asset::by_account_id(mouse_id.clone()),
|result| {
let assets = result.collect::<QueryResult<Vec<_>>>().unwrap();
assets.iter().any(|asset| {
asset.id().definition_id == asset_definition_id
&& asset.id().account_id == mouse_id
})
},
)
.expect("Test case failure.");
}

fn simulate_transfer<T>(
starting_amount: T,
amount_to_transfer: &T,
Expand All @@ -74,13 +129,9 @@ fn simulate_transfer<T>(
.start_with_runtime();
wait_for_genesis_committed(&[iroha_client.clone()], 0);

let alice_id: AccountId = "alice@wonderland".parse().expect("Valid");
let mouse_id: AccountId = "mouse@wonderland".parse().expect("Valid");
let (bob_public_key, _) = KeyPair::generate()
.expect("Failed to generate KeyPair")
.into();
let create_mouse = Register::account(Account::new(mouse_id.clone(), [bob_public_key]));
let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().expect("Valid");
let (alice_id, mouse_id) = generate_two_ids();
let create_mouse = create_mouse(mouse_id.clone());
let asset_definition_id: AssetDefinitionId = "camomile#wonderland".parse().unwrap();
let create_asset =
Register::asset_definition(asset_definition_ctr(asset_definition_id.clone()));
let mint_asset = mint_ctr(
Expand Down Expand Up @@ -109,7 +160,7 @@ fn simulate_transfer<T>(
transfer_asset,
client::asset::by_account_id(mouse_id.clone()),
|result| {
let assets = result.collect::<QueryResult<Vec<_>>>().expect("Valid");
let assets = result.collect::<QueryResult<Vec<_>>>().unwrap();

assets.iter().any(|asset| {
asset.id().definition_id == asset_definition_id
Expand All @@ -120,3 +171,16 @@ fn simulate_transfer<T>(
)
.expect("Test case failure.");
}

fn generate_two_ids() -> (AccountId, AccountId) {
let alice_id: AccountId = "alice@wonderland".parse().unwrap();
let mouse_id: AccountId = "mouse@wonderland".parse().unwrap();
(alice_id, mouse_id)
}

fn create_mouse(mouse_id: AccountId) -> Register<Account> {
let (mouse_public_key, _) = KeyPair::generate()
.expect("Failed to generate KeyPair")
.into();
Register::account(Account::new(mouse_id, [mouse_public_key]))
}
Binary file modified configs/peer/executor.wasm
Binary file not shown.
33 changes: 32 additions & 1 deletion core/src/smartcontracts/isi/asset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Registrable for NewAssetDefinition {
/// - update metadata
/// - transfer, etc.
pub mod isi {
use iroha_data_model::isi::error::MintabilityError;
use iroha_data_model::{asset::AssetValue, isi::error::MintabilityError, metadata::Metadata};

use super::*;
use crate::smartcontracts::account::isi::forbid_minting;
Expand Down Expand Up @@ -103,6 +103,37 @@ pub mod isi {
}
}

impl Execute for Transfer<Asset, Metadata, Account> {
#[metrics(+"transfer_store")]
fn execute(self, _authority: &AccountId, wsv: &mut WorldStateView) -> Result<(), Error> {
let asset_id = self.source_id;
assert_asset_type(&asset_id.definition_id, wsv, AssetValueType::Store)?;
let account_id = asset_id.account_id.clone();

let asset = wsv.account_mut(&account_id).and_then(|account| {
account
.remove_asset(&asset_id)
.ok_or_else(|| FindError::Asset(asset_id.clone()))
})?;

let destination_store = {
let destination_id =
AssetId::new(asset_id.definition_id.clone(), self.destination_id.clone());
let destination_store_asset =
wsv.asset_or_insert(destination_id.clone(), asset.value)?;

destination_store_asset.clone()
};

wsv.emit_events([
AssetEvent::Deleted(asset_id),
AssetEvent::Created(destination_store),
]);

Ok(())
}
}

macro_rules! impl_mint {
($ty:ty, $metrics:literal) => {
impl InnerMint for $ty {}
Expand Down
1 change: 1 addition & 0 deletions core/src/smartcontracts/isi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ impl Execute for AssetTransferBox {
Self::Quantity(isi) => isi.execute(authority, wsv),
Self::BigQuantity(isi) => isi.execute(authority, wsv),
Self::Fixed(isi) => isi.execute(authority, wsv),
Self::Store(isi) => isi.execute(authority, wsv),
}
}
}
Expand Down
18 changes: 17 additions & 1 deletion data_model/src/isi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ pub mod model {
impl Instruction for Transfer<Asset, u32, Account> {}
impl Instruction for Transfer<Asset, u128, Account> {}
impl Instruction for Transfer<Asset, Fixed, Account> {}
impl Instruction for Transfer<Asset, Metadata, Account> {}

impl Instruction for Grant<PermissionToken> {}
impl Instruction for Grant<RoleId> {}
Expand All @@ -166,7 +167,7 @@ pub mod model {

mod transparent {
use super::*;
use crate::{account::NewAccount, domain::NewDomain};
use crate::{account::NewAccount, domain::NewDomain, metadata::Metadata};

macro_rules! isi {
($($meta:meta)* $item:item) => {
Expand Down Expand Up @@ -847,6 +848,17 @@ mod transparent {
}
}

impl Transfer<Asset, Metadata, Account> {
/// Constructs a new [`Transfer`] for an [`Asset`] of [`Store`] type.
pub fn asset_store(asset_id: AssetId, to: AccountId) -> Self {
Self {
source_id: asset_id,
object: Metadata::new(),
destination_id: to,
}
}
}

impl_display! {
Transfer<S, O, D>
where
Expand All @@ -865,6 +877,7 @@ mod transparent {
impl_into_box! {
Transfer<Asset, u32, Account> |
Transfer<Asset, u128, Account> |
Transfer<Asset, Metadata, Account> |
Transfer<Asset, Fixed, Account> => AssetTransferBox ==> TransferBox::Asset
}

Expand All @@ -873,6 +886,7 @@ mod transparent {
Transfer<Account, AssetDefinitionId, Account> |
Transfer<Asset, u32, Account> |
Transfer<Asset, u128, Account> |
Transfer<Asset, Metadata, Account> |
Transfer<Asset, Fixed, Account> => TransferBox ==> InstructionBox::Transfer
}

Expand Down Expand Up @@ -1194,6 +1208,8 @@ isi_box! {
BigQuantity(Transfer<Asset, u128, Account>),
/// Transfer [`Asset`] of [`Fixed`] type.
Fixed(Transfer<Asset, Fixed, Account>),
/// Transfer [`Asset`] of [`Store`] type.
Store(Transfer<Asset, Metadata, Account>),
}
}

Expand Down
1 change: 1 addition & 0 deletions data_model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ mod seal {
Transfer<Asset, u32, Account>,
Transfer<Asset, u128, Account>,
Transfer<Asset, Fixed, Account>,
Transfer<Asset, Metadata, Account>,

Grant<PermissionToken>,
Grant<RoleId>,
Expand Down
3 changes: 3 additions & 0 deletions data_model/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub trait Visit {
visit_transfer_asset_quantity(&Transfer<Asset, u32, Account>),
visit_transfer_asset_big_quantity(&Transfer<Asset, u128, Account>),
visit_transfer_asset_fixed(&Transfer<Asset, Fixed, Account>),
visit_transfer_asset_store(&Transfer<Asset, Metadata, Account>),
visit_transfer_domain(&Transfer<Account, DomainId, Account>),

// Visit SetKeyValueBox
Expand Down Expand Up @@ -349,6 +350,7 @@ pub fn visit_transfer<V: Visit + ?Sized>(
visitor.visit_transfer_asset_big_quantity(authority, obj)
}
AssetTransferBox::Fixed(obj) => visitor.visit_transfer_asset_fixed(authority, obj),
AssetTransferBox::Store(obj) => visitor.visit_transfer_asset_store(authority, obj),
},
}
}
Expand Down Expand Up @@ -425,6 +427,7 @@ leaf_visitors! {
visit_transfer_asset_quantity(&Transfer<Asset, u32, Account>),
visit_transfer_asset_big_quantity(&Transfer<Asset, u128, Account>),
visit_transfer_asset_fixed(&Transfer<Asset, Fixed, Account>),
visit_transfer_asset_store(&Transfer<Asset, Metadata, Account>),
visit_set_asset_key_value(&SetKeyValue<Asset>),
visit_remove_asset_key_value(&RemoveKeyValue<Asset>),
visit_register_asset_definition(&Register<AssetDefinition>),
Expand Down
21 changes: 21 additions & 0 deletions docs/source/references/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,11 @@
"tag": "Fixed",
"discriminant": 2,
"type": "Transfer<Asset, Fixed, Account>"
},
{
"tag": "Store",
"discriminant": 3,
"type": "Transfer<Asset, Metadata, Account>"
}
]
},
Expand Down Expand Up @@ -4253,6 +4258,22 @@
}
]
},
"Transfer<Asset, Metadata, Account>": {
"Struct": [
{
"name": "source_id",
"type": "AssetId"
},
{
"name": "object",
"type": "Metadata"
},
{
"name": "destination_id",
"type": "AccountId"
}
]
},
"Transfer<Asset, u128, Account>": {
"Struct": [
{
Expand Down
1 change: 1 addition & 0 deletions smart_contract/executor/derive/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub fn impl_derive_visit(emitter: &mut Emitter, input: &syn2::DeriveInput) -> To
"fn visit_transfer_asset_quantity(operation: &Transfer<Asset, u32, Account>)",
"fn visit_transfer_asset_big_quantity(operation: &Transfer<Asset, u128, Account>)",
"fn visit_transfer_asset_fixed(operation: &Transfer<Asset, Fixed, Account>)",
"fn visit_transfer_asset_store(operation: &Transfer<Asset, Metadata, Account>)",
"fn visit_set_asset_key_value(operation: &SetKeyValue<Asset>)",
"fn visit_remove_asset_key_value(operation: &RemoveKeyValue<Asset>)",
"fn visit_register_asset_definition(operation: &Register<AssetDefinition>)",
Expand Down
12 changes: 10 additions & 2 deletions smart_contract/executor/src/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use asset::{
visit_mint_asset_big_quantity, visit_mint_asset_fixed, visit_mint_asset_quantity,
visit_register_asset, visit_remove_asset_key_value, visit_set_asset_key_value,
visit_transfer_asset_big_quantity, visit_transfer_asset_fixed, visit_transfer_asset_quantity,
visit_unregister_asset,
visit_transfer_asset_store, visit_unregister_asset,
};
pub use asset_definition::{
visit_register_asset_definition, visit_remove_asset_definition_key_value,
Expand Down Expand Up @@ -919,7 +919,7 @@ pub mod asset_definition {
}

pub mod asset {
use iroha_smart_contract::data_model::isi::Instruction;
use iroha_smart_contract::data_model::{isi::Instruction, metadata::Metadata};
use iroha_smart_contract_utils::Encode;
use permission::{asset::is_asset_owner, asset_definition::is_asset_definition_owner};

Expand Down Expand Up @@ -1173,6 +1173,14 @@ pub mod asset {
validate_transfer_asset(executor, authority, isi);
}

pub fn visit_transfer_asset_store<V: Validate + ?Sized>(
executor: &mut V,
authority: &AccountId,
isi: &Transfer<Asset, Metadata, Account>,
) {
validate_transfer_asset(executor, authority, isi);
}

pub fn visit_set_asset_key_value<V: Validate + ?Sized>(
executor: &mut V,
authority: &AccountId,
Expand Down

0 comments on commit e3a4f42

Please sign in to comment.