Skip to content

Commit

Permalink
record mint amount
Browse files Browse the repository at this point in the history
  • Loading branch information
yito88 committed Feb 22, 2024
1 parent ab508c0 commit a81633d
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/scripts/e2e.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"e2e::ibc_tests::run_ledger_ibc": 155,
"e2e::ibc_tests::run_ledger_ibc_with_hermes": 130,
"e2e::ibc_tests::pgf_over_ibc_with_hermes": 240,
"e2e::ibc_tests::ibc_rate_limit": 500,
"e2e::eth_bridge_tests::test_add_to_bridge_pool": 10,
"e2e::ledger_tests::double_signing_gets_slashed": 12,
"e2e::ledger_tests::invalid_transactions": 13,
Expand Down Expand Up @@ -36,4 +37,4 @@
"e2e::wallet_tests::wallet_encrypted_key_cmds": 1,
"e2e::wallet_tests::wallet_encrypted_key_cmds_env_var": 1,
"e2e::wallet_tests::wallet_unencrypted_key_cmds": 1
}
}
16 changes: 16 additions & 0 deletions crates/ibc/src/context/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,22 @@ pub trait IbcCommonContext: IbcStorageContext {
Ok(amount == Some(Amount::from_u64(1)))
}

/// Read the mint amount of the given token
fn mint_amount(&self, token: &Address) -> Result<Amount> {
let key = storage::mint_amount_key(token);
Ok(self.read::<Amount>(&key)?.unwrap_or_default())
}

/// Write the mint amount of the given token
fn store_mint_amount(
&mut self,
token: &Address,
amount: Amount,
) -> Result<()> {
let key = storage::mint_amount_key(token);
self.write(&key, amount).map_err(ContextError::from)
}

/// Read the per-epoch deposit of the given token
fn deposit(&self, token: &Address) -> Result<Amount> {
let key = storage::deposit_key(token);
Expand Down
28 changes: 28 additions & 0 deletions crates/ibc/src/context/nft_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,32 @@ where
Self { inner }
}

/// Update the mint amount of the token
fn update_mint_amount(
&self,
token: &Address,
is_minted: bool,
) -> Result<(), NftTransferError> {
let mint = self.inner.borrow().mint_amount(token)?;
let updated_mint = if is_minted {
mint.checked_add(Amount::from_u64(1)).ok_or_else(|| {
NftTransferError::Other(
"The mint amount overflowed".to_string(),
)
})?
} else {
mint.checked_sub(Amount::from_u64(1)).ok_or_else(|| {
NftTransferError::Other(
"The mint amount underflowed".to_string(),
)
})?
};
self.inner
.borrow_mut()
.store_mint_amount(token, updated_mint)
.map_err(NftTransferError::from)
}

/// Add the amount to the per-epoch withdraw of the token
fn add_deposit(&self, token: &Address) -> Result<(), NftTransferError> {
let deposit = self.inner.borrow().deposit(token)?;
Expand Down Expand Up @@ -317,6 +343,7 @@ where
};
self.inner.borrow_mut().store_nft_metadata(metadata)?;

self.update_mint_amount(&ibc_token, true)?;
self.add_deposit(&ibc_token)?;

self.inner
Expand All @@ -334,6 +361,7 @@ where
) -> Result<(), NftTransferError> {
let ibc_token = storage::ibc_token_for_nft(class_id, token_id);

self.update_mint_amount(&ibc_token, false)?;
self.add_withdraw(&ibc_token)?;

self.inner
Expand Down
29 changes: 29 additions & 0 deletions crates/ibc/src/context/token_transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,33 @@ where
Ok((token, amount))
}

/// Update the mint amount of the token
fn update_mint_amount(
&self,
token: &Address,
amount: Amount,
is_minted: bool,
) -> Result<(), TokenTransferError> {
let mint = self.inner.borrow().mint_amount(token)?;
let updated_mint = if is_minted {
mint.checked_add(amount).ok_or_else(|| {
TokenTransferError::Other(
"The mint amount overflowed".to_string(),
)
})?
} else {
mint.checked_sub(amount).ok_or_else(|| {
TokenTransferError::Other(
"The mint amount underflowed".to_string(),
)
})?
};
self.inner
.borrow_mut()
.store_mint_amount(token, updated_mint)
.map_err(TokenTransferError::from)
}

/// Add the amount to the per-epoch withdraw of the token
fn add_deposit(
&self,
Expand Down Expand Up @@ -223,6 +250,7 @@ where
// The trace path of the denom is already updated if receiving the token
let (ibc_token, amount) = self.get_token_amount(coin)?;

self.update_mint_amount(&ibc_token, amount, true)?;
self.add_deposit(&ibc_token, amount)?;

self.inner
Expand All @@ -239,6 +267,7 @@ where
) -> Result<(), TokenTransferError> {
let (ibc_token, amount) = self.get_token_amount(coin)?;

self.update_mint_amount(&ibc_token, amount, false)?;
self.add_withdraw(&ibc_token, amount)?;

// The burn is "unminting" from the minted balance
Expand Down
13 changes: 12 additions & 1 deletion crates/ibc/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const NFT_CLASS: &str = "nft_class";
const NFT_METADATA: &str = "nft_meta";
const PARAMS: &str = "params";
const MINT_LIMIT: &str = "mint_limit";
const MINT: &str = "mint";
const THROUGHPUT_LIMIT: &str = "throughput_limit";
const DEPOSIT: &str = "deposit";
const WITHDRAW: &str = "withdraw";
Expand Down Expand Up @@ -496,7 +497,7 @@ pub fn params_key() -> Key {
.expect("Cannot obtain a storage key")
}

/// Returns a key of the deposit limit for the token
/// Returns a key of the mint limit for the token
pub fn mint_limit_key(token: &Address) -> Key {
Key::from(Address::Internal(InternalAddress::Ibc).to_db_key())
.push(&MINT_LIMIT.to_string().to_db_key())
Expand All @@ -506,6 +507,16 @@ pub fn mint_limit_key(token: &Address) -> Key {
.expect("Cannot obtain a storage key")
}

/// Returns a key of the IBC mint amount for the token
pub fn mint_amount_key(token: &Address) -> Key {
Key::from(Address::Internal(InternalAddress::Ibc).to_db_key())
.push(&MINT.to_string().to_db_key())
.expect("Cannot obtain a storage key")
// Set as String to avoid checking the token address
.push(&token.to_string().to_db_key())
.expect("Cannot obtain a storage key")
}

/// Returns a key of the per-epoch throughput limit for the token
pub fn throughput_limit_key(token: &Address) -> Key {
Key::from(Address::Internal(InternalAddress::Ibc).to_db_key())
Expand Down
10 changes: 5 additions & 5 deletions crates/namada/src/ledger/native_vp/ibc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ use thiserror::Error;

use crate::ibc::core::host::types::identifiers::ChainId as IbcChainId;
use crate::ledger::ibc::storage::{
calc_hash, deposit_key, is_ibc_key, is_ibc_trace_key, mint_limit_key,
params_key, throughput_limit_key, withdraw_key,
calc_hash, deposit_key, is_ibc_key, is_ibc_trace_key, mint_amount_key,
mint_limit_key, params_key, throughput_limit_key, withdraw_key,
};
use crate::ledger::native_vp::{self, Ctx, NativeVp};
use crate::ledger::parameters::read_epoch_duration_parameter;
use crate::token::storage_key::{is_any_token_balance_key, minted_balance_key};
use crate::token::storage_key::is_any_token_balance_key;
use crate::types::address::Address;
use crate::types::token::Amount;
use crate::vm::WasmCacheAccess;
Expand Down Expand Up @@ -264,10 +264,10 @@ where
};

// Check the supply
let minted_balance_key = minted_balance_key(token);
let mint_amount_key = mint_amount_key(token);
let minted: Amount = self
.ctx
.read_post(&minted_balance_key)
.read_post(&mint_amount_key)
.map_err(Error::NativeVpError)?
.unwrap_or_default();
if mint_limit < minted {
Expand Down
71 changes: 43 additions & 28 deletions crates/tests/src/e2e/ibc_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,35 +460,44 @@ fn pgf_over_ibc_with_hermes() -> Result<()> {

#[test]
fn ibc_rate_limit() -> Result<()> {
// Mint limit 2 NAM, per-epoch throughput limit 1 NAM
let update_genesis =
|mut genesis: templates::All<templates::Unvalidated>, base_dir: &_| {
genesis.parameters.parameters.epochs_per_year =
epochs_per_year_from_min_duration(10);
// for the trusting period of IBC client
genesis.parameters.pos_params.pipeline_len = 10;
genesis.parameters.ibc_params.default_mint_limit =
Amount::from_u64(2_000_000);
genesis
.parameters
.ibc_params
.default_per_epoch_throughput_limit =
Amount::from_u64(1_000_000);
setup::set_validators(1, genesis, base_dir, |_| 0)
};
// Mint limit 2 transfer/channel-0/nam, per-epoch throughput limit 1 NAM
let update_genesis = |mut genesis: templates::All<
templates::Unvalidated,
>,
base_dir: &_| {
genesis.parameters.parameters.epochs_per_year =
epochs_per_year_from_min_duration(50);
genesis.parameters.ibc_params.default_mint_limit = Amount::from_u64(2);
genesis
.parameters
.ibc_params
.default_per_epoch_throughput_limit = Amount::from_u64(1_000_000);
setup::set_validators(1, genesis, base_dir, |_| 0)
};
let (ledger_a, ledger_b, test_a, test_b) = run_two_nets(update_genesis)?;
let _bg_ledger_a = ledger_a.background();
let _bg_ledger_b = ledger_b.background();

setup_hermes(&test_a, &test_b)?;
let port_id_a = "transfer".parse().unwrap();
let (channel_id_a, _channel_id_b) =
let port_id_b: PortId = "transfer".parse().unwrap();
let (channel_id_a, channel_id_b) =
create_channel_with_hermes(&test_a, &test_b)?;

// Start relaying
let hermes = run_hermes(&test_a)?;
let _bg_hermes = hermes.background();

// wait for the next epoch
std::env::set_var(ENV_VAR_CHAIN_ID, test_a.net.chain_id.to_string());
let rpc_a = get_actor_rpc(&test_a, Who::Validator(0));
let mut epoch = get_epoch(&test_a, &rpc_a).unwrap();
let next_epoch = epoch.next();
while epoch <= next_epoch {
sleep(5);
epoch = get_epoch(&test_a, &rpc_a).unwrap();
}

// Transfer 1 NAM from Chain A to Chain B
std::env::set_var(ENV_VAR_CHAIN_ID, test_b.net.chain_id.to_string());
let receiver = find_address(&test_b, BERTHA)?;
Expand All @@ -505,8 +514,6 @@ fn ibc_rate_limit() -> Result<()> {
None,
false,
)?;
// This transfer should succeed
wait_for_packet_relay(&port_id_a, &channel_id_a, &test_a)?;

// Transfer 1 NAM from Chain A to Chain B again will fail
transfer(
Expand All @@ -520,20 +527,19 @@ fn ibc_rate_limit() -> Result<()> {
&channel_id_a,
None,
// expect an error of the throughput limit
Some(&format!("throughput limit")),
Some("Transaction was rejected by VPs"),
false,
)?;

// wait for the next epoch
let rpc_a = get_actor_rpc(&test_a, Who::Validator(0));
let mut epoch = get_epoch(&test_a, &rpc_a).unwrap();
let next_epoch = epoch.next();
while epoch <= next_epoch {
sleep(5);
epoch = get_epoch(&test_a, &rpc_a).unwrap();
}

// Transfer 1 NAM from Chain A to Chain B will succeed
// Transfer 1 NAM from Chain A to Chain B will succeed in the new epoch
transfer(
&test_a,
ALBERT,
Expand All @@ -547,8 +553,6 @@ fn ibc_rate_limit() -> Result<()> {
None,
false,
)?;
// This transfer should succeed
wait_for_packet_relay(&port_id_a, &channel_id_a, &test_a)?;

// wait for the next epoch
let mut epoch = get_epoch(&test_a, &rpc_a).unwrap();
Expand All @@ -558,7 +562,8 @@ fn ibc_rate_limit() -> Result<()> {
epoch = get_epoch(&test_a, &rpc_a).unwrap();
}

// Transfer 1 NAM from Chain A to Chain B will fail
// Transfer 1 NAM from Chain A to Chain B will succeed, but Chain B can't
// receive due to the mint limit and the packet will be timed out
transfer(
&test_a,
ALBERT,
Expand All @@ -568,14 +573,24 @@ fn ibc_rate_limit() -> Result<()> {
ALBERT_KEY,
&port_id_a,
&channel_id_a,
Some(Duration::new(20, 0)),
None,
// expect an error of the mint limit
Some(&format!("mint limit")),
false,
)?;
// This transfer should succeed
wait_for_packet_relay(&port_id_a, &channel_id_a, &test_a)?;

// Check the balance on Chain B
let ibc_denom = format!("{port_id_b}/{channel_id_b}/nam");
std::env::set_var(ENV_VAR_CHAIN_ID, test_b.net.chain_id.to_string());
let rpc_b = get_actor_rpc(&test_b, Who::Validator(0));
let query_args = vec![
"balance", "--owner", BERTHA, "--token", &ibc_denom, "--node", &rpc_b,
];
let expected = format!("{ibc_denom}: 2");
let mut client = run!(test_b, Bin::Client, query_args, Some(40))?;
client.exp_string(&expected)?;
client.assert_success();

Ok(())
}

Expand Down

0 comments on commit a81633d

Please sign in to comment.