Skip to content
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!: Introduce fee receiver #5323

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions crates/iroha/tests/fee.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
use eyre::Result;
use iroha::data_model::prelude::*;
use iroha_data_model::fee::FeeReceiverDefinition;
use iroha_test_network::*;
use iroha_test_samples::{ALICE_ID, BOB_ID};

#[test]
fn fee_receiver_declared_in_genesis() -> Result<()> {
let alice_id = ALICE_ID.clone();
let cabbage_id: AssetDefinitionId = "cabbage#garden_of_live_flowers".parse()?;

let declare_fee_receiver = Declare::fee_receiver(FeeReceiverDefinition::new(
alice_id.clone(),
cabbage_id.clone(),
));

let (network, _rt) = NetworkBuilder::new()
.with_genesis_instruction(declare_fee_receiver)
.start_blocking()?;

let test_client = network.client();

let result = test_client.query_single(FindFeeReceiverAccount::new())?;
let fees_recipient = result.expect("Fee receiver must be declared");
assert_eq!(fees_recipient, alice_id);

let result = test_client.query_single(FindFeePaymentAsset::new())?;
let fees_asset = result.expect("Fee asset must be declared");
assert_eq!(fees_asset, cabbage_id);

Ok(())
}

#[test]
fn fee_receiver_absent_in_default_genesis() -> Result<()> {
let (network, _rt) = NetworkBuilder::new().start_blocking()?;

let test_client = network.client();

let result = test_client.query_single(FindFeeReceiverAccount::new())?;
assert!(result.is_none());

let result = test_client.query_single(FindFeePaymentAsset::new())?;
assert!(result.is_none());

Ok(())
}

#[test]
fn fee_receiver_cannot_change() -> Result<()> {
let alice_id = ALICE_ID.clone();
let bob_id = BOB_ID.clone();
let cabbage_id: AssetDefinitionId = "cabbage#garden_of_live_flowers".parse()?;
let rose_id: AssetDefinitionId = "rose#wonderland".parse()?;

let declare_fee_receiver = Declare::fee_receiver(FeeReceiverDefinition::new(
alice_id.clone(),
cabbage_id.clone(),
));
let redeclare_fee_receiver =
Declare::fee_receiver(FeeReceiverDefinition::new(bob_id.clone(), rose_id.clone()));

let (network, _rt) = NetworkBuilder::new()
.with_genesis_instruction(declare_fee_receiver)
.with_genesis_instruction(redeclare_fee_receiver)
.start_blocking()?;

let test_client = network.client();

let result = test_client.query_single(FindFeeReceiverAccount::new())?;
let fees_recipient = result.expect("Fee receiver must be declared");
assert_eq!(fees_recipient, alice_id);

let result = test_client.query_single(FindFeePaymentAsset::new())?;
let fees_asset = result.expect("Fee asset must be declared");
assert_eq!(fees_asset, cabbage_id);

Ok(())
}

#[test]
fn fee_receiver_cannot_declare_outside_genesis() -> Result<()> {
let alice_id = ALICE_ID.clone();
let cabbage_id: AssetDefinitionId = "cabbage#garden_of_live_flowers".parse()?;

let declare_fee_receiver = Declare::fee_receiver(FeeReceiverDefinition::new(
alice_id.clone(),
cabbage_id.clone(),
));

let (network, _rt) = NetworkBuilder::new().start_blocking()?;

let test_client = network.client();

let result = test_client.submit_blocking(declare_fee_receiver);

assert!(result.is_err());

Ok(())
}

#[test]
fn fee_receiver_shared() -> Result<()> {
let alice_id = ALICE_ID.clone();
let cabbage_id: AssetDefinitionId = "cabbage#garden_of_live_flowers".parse()?;

let declare_fee_receiver = Declare::fee_receiver(FeeReceiverDefinition::new(
alice_id.clone(),
cabbage_id.clone(),
));

let (network, _rt) = NetworkBuilder::new()
.with_genesis_instruction(declare_fee_receiver)
.with_peers(3)
.start_blocking()?;

let mut peers = network.peers().iter();
let peer_a = peers.next().unwrap();
let peer_b = peers.next().unwrap();

let test_client = peer_a.client();

let result = test_client.query_single(FindFeeReceiverAccount::new())?;
let fees_recipient = result.expect("Fee receiver must be declared");
assert_eq!(fees_recipient, alice_id);

let result = test_client.query_single(FindFeePaymentAsset::new())?;
let fees_asset = result.expect("Fee asset must be declared");
assert_eq!(fees_asset, cabbage_id);

let test_client = peer_b.client();

let result = test_client.query_single(FindFeeReceiverAccount::new())?;
let fees_recipient = result.expect("Fee receiver must be declared");
assert_eq!(fees_recipient, alice_id);

let result = test_client.query_single(FindFeePaymentAsset::new())?;
let fees_asset = result.expect("Fee asset must be declared");
assert_eq!(fees_asset, cabbage_id);

Ok(())
}
14 changes: 14 additions & 0 deletions crates/iroha_core/src/smartcontracts/isi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl Execute for InstructionBox {
Self::RemoveKeyValue(isi) => isi.execute(authority, state_transaction),
Self::Grant(isi) => isi.execute(authority, state_transaction),
Self::Revoke(isi) => isi.execute(authority, state_transaction),
Self::Declare(isi) => isi.execute(authority, state_transaction),
Self::ExecuteTrigger(isi) => isi.execute(authority, state_transaction),
Self::SetParameter(isi) => isi.execute(authority, state_transaction),
Self::Upgrade(isi) => isi.execute(authority, state_transaction),
Expand Down Expand Up @@ -218,6 +219,19 @@ impl Execute for RevokeBox {
}
}

impl Execute for DeclareBox {
#[iroha_logger::log(name = "declare", skip_all, fields(object))]
fn execute(
self,
authority: &AccountId,
state_transaction: &mut StateTransaction<'_, '_>,
) -> Result<(), Error> {
match self {
Self::FeeReceiver(sub_isi) => sub_isi.execute(authority, state_transaction),
}
}
}

pub mod prelude {
//! Re-export important traits and types for glob import `(::*)`
pub use super::*;
Expand Down
6 changes: 6 additions & 0 deletions crates/iroha_core/src/smartcontracts/isi/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ impl ValidQueryRequest {
SingularQueryBox::FindParameters(q) => {
SingularQueryOutputBox::from(q.execute(state)?)
}
SingularQueryBox::FindFeeReceiverAccount(q) => {
SingularQueryOutputBox::from(q.execute(state)?)
}
SingularQueryBox::FindFeePaymentAsset(q) => {
SingularQueryOutputBox::from(q.execute(state)?)
}
};

Ok(QueryResponse::Singular(output))
Expand Down
58 changes: 58 additions & 0 deletions crates/iroha_core/src/smartcontracts/isi/world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,37 @@ pub mod isi {
}
}

impl Execute for Declare<FeeReceiverDefinition> {
#[metrics(+"fee_receiver")]
fn execute(
self,
_authority: &AccountId,
state_transaction: &mut StateTransaction<'_, '_>,
) -> Result<(), Error> {
let fee_receiver = self.object;

state_transaction.world.account(&fee_receiver.account)?;
state_transaction
.world
.asset_definition(&fee_receiver.asset)?;

state_transaction
.world
.fee_receiver
.get_or_insert(fee_receiver.clone());

let event: DataEvent = FeeEvent::RecepientUpdated(FeeReceiverUpdated {
account: fee_receiver.account,
asset: fee_receiver.asset,
})
.into();

state_transaction.world.emit_events(Some(event));

Ok(())
}
}

impl Execute for SetParameter {
#[metrics(+"set_parameter")]
fn execute(
Expand Down Expand Up @@ -522,4 +553,31 @@ pub mod query {
Ok(state_ro.world().parameters().clone())
}
}

impl ValidSingularQuery for FindFeeReceiverAccount {
#[metrics(+"find_fee_receiver")]
fn execute(&self, state_ro: &impl StateReadOnly) -> Result<Option<AccountId>, Error> {
let fee_receiver = state_ro
.world()
.fee_receiever()
.clone()
.map(|frd| frd.account);
Ok(fee_receiver)
}
}

impl ValidSingularQuery for FindFeePaymentAsset {
#[metrics(+"find_fee_asset")]
fn execute(
&self,
state_ro: &impl StateReadOnly,
) -> Result<Option<AssetDefinitionId>, Error> {
let fee_asset = state_ro
.world()
.fee_receiever()
.clone()
.map(|frd| frd.asset);
Ok(fee_asset)
}
}
}
26 changes: 26 additions & 0 deletions crates/iroha_core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ pub struct World {
pub(crate) account_permissions: Storage<AccountId, Permissions>,
/// Roles of an account.
pub(crate) account_roles: Storage<RoleIdWithOwner, ()>,
/// Fees
pub(crate) fee_receiver: Cell<Option<FeeReceiverDefinition>>,
/// Triggers
pub(crate) triggers: TriggerSet,
/// Runtime Executor
Expand Down Expand Up @@ -109,6 +111,8 @@ pub struct WorldBlock<'world> {
pub(crate) account_permissions: StorageBlock<'world, AccountId, Permissions>,
/// Roles of an account.
pub(crate) account_roles: StorageBlock<'world, RoleIdWithOwner, ()>,
/// Fee receiver if declared in network.
pub(crate) fee_receiver: CellBlock<'world, Option<FeeReceiverDefinition>>,
/// Triggers
pub(crate) triggers: TriggerSetBlock<'world>,
/// Runtime Executor
Expand Down Expand Up @@ -140,6 +144,8 @@ pub struct WorldTransaction<'block, 'world> {
pub(crate) account_permissions: StorageTransaction<'block, 'world, AccountId, Permissions>,
/// Roles of an account.
pub(crate) account_roles: StorageTransaction<'block, 'world, RoleIdWithOwner, ()>,
/// Fee receiver if declared in network.
pub(crate) fee_receiver: CellTransaction<'block, 'world, Option<FeeReceiverDefinition>>,
/// Triggers
pub(crate) triggers: TriggerSetTransaction<'block, 'world>,
/// Runtime Executor
Expand Down Expand Up @@ -178,6 +184,8 @@ pub struct WorldView<'world> {
pub(crate) account_permissions: StorageView<'world, AccountId, Permissions>,
/// Roles of an account.
pub(crate) account_roles: StorageView<'world, RoleIdWithOwner, ()>,
/// Fee receiver if declared in network.
pub(crate) fee_receiver: CellView<'world, Option<FeeReceiverDefinition>>,
/// Triggers
pub(crate) triggers: TriggerSetView<'world>,
/// Runtime Executor
Expand Down Expand Up @@ -360,6 +368,7 @@ impl World {
roles: self.roles.block(),
account_permissions: self.account_permissions.block(),
account_roles: self.account_roles.block(),
fee_receiver: self.fee_receiver.block(),
triggers: self.triggers.block(),
executor: self.executor.block(),
executor_data_model: self.executor_data_model.block(),
Expand All @@ -379,6 +388,7 @@ impl World {
roles: self.roles.block_and_revert(),
account_permissions: self.account_permissions.block_and_revert(),
account_roles: self.account_roles.block_and_revert(),
fee_receiver: self.fee_receiver.block_and_revert(),
triggers: self.triggers.block_and_revert(),
executor: self.executor.block_and_revert(),
executor_data_model: self.executor_data_model.block_and_revert(),
Expand All @@ -398,6 +408,7 @@ impl World {
roles: self.roles.view(),
account_permissions: self.account_permissions.view(),
account_roles: self.account_roles.view(),
fee_receiver: self.fee_receiver.view(),
triggers: self.triggers.view(),
executor: self.executor.view(),
executor_data_model: self.executor_data_model.view(),
Expand All @@ -417,6 +428,7 @@ pub trait WorldReadOnly {
fn roles(&self) -> &impl StorageReadOnly<RoleId, Role>;
fn account_permissions(&self) -> &impl StorageReadOnly<AccountId, Permissions>;
fn account_roles(&self) -> &impl StorageReadOnly<RoleIdWithOwner, ()>;
fn fee_receiever(&self) -> &Option<FeeReceiverDefinition>;
fn triggers(&self) -> &impl TriggerSetReadOnly;
fn executor(&self) -> &Executor;
fn executor_data_model(&self) -> &ExecutorDataModel;
Expand Down Expand Up @@ -679,6 +691,9 @@ macro_rules! impl_world_ro {
fn account_roles(&self) -> &impl StorageReadOnly<RoleIdWithOwner, ()> {
&self.account_roles
}
fn fee_receiever(&self) -> &Option<FeeReceiverDefinition> {
&self.fee_receiver
}
fn triggers(&self) -> &impl TriggerSetReadOnly {
&self.triggers
}
Expand Down Expand Up @@ -709,6 +724,7 @@ impl<'world> WorldBlock<'world> {
roles: self.roles.transaction(),
account_permissions: self.account_permissions.transaction(),
account_roles: self.account_roles.transaction(),
fee_receiver: self.fee_receiver.transaction(),
triggers: self.triggers.transaction(),
executor: self.executor.transaction(),
executor_data_model: self.executor_data_model.transaction(),
Expand All @@ -732,6 +748,7 @@ impl<'world> WorldBlock<'world> {
roles,
account_permissions,
account_roles,
fee_receiver,
triggers,
executor,
executor_data_model,
Expand All @@ -749,6 +766,7 @@ impl<'world> WorldBlock<'world> {
accounts.commit();
domains.commit();
peers.commit();
fee_receiver.commit();
parameters.commit();
}
}
Expand All @@ -767,6 +785,7 @@ impl WorldTransaction<'_, '_> {
roles,
account_permissions,
account_roles,
fee_receiver,
triggers,
executor,
executor_data_model,
Expand All @@ -783,6 +802,7 @@ impl WorldTransaction<'_, '_> {
accounts.apply();
domains.apply();
peers.apply();
fee_receiver.apply();
parameters.apply();
events_buffer.events_created_in_transaction = 0;
}
Expand Down Expand Up @@ -1873,6 +1893,7 @@ pub(crate) mod deserialize {
let mut roles = None;
let mut account_permissions = None;
let mut account_roles = None;
let mut fee_receiver = None;
let mut triggers = None;
let mut executor = None;
let mut executor_data_model = None;
Expand Down Expand Up @@ -1906,6 +1927,9 @@ pub(crate) mod deserialize {
"account_roles" => {
account_roles = Some(map.next_value()?);
}
"fee_receiver" => {
fee_receiver = Some(map.next_value()?);
}
"triggers" => {
triggers =
Some(map.next_value_seed(self.loader.cast::<TriggerSet>())?);
Expand Down Expand Up @@ -1940,6 +1964,8 @@ pub(crate) mod deserialize {
})?,
account_roles: account_roles
.ok_or_else(|| serde::de::Error::missing_field("account_roles"))?,
fee_receiver: fee_receiver
.ok_or_else(|| serde::de::Error::missing_field("fee_receiver"))?,
triggers: triggers
.ok_or_else(|| serde::de::Error::missing_field("triggers"))?,
executor: executor
Expand Down
Loading
Loading