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

converts faucet_withdrawal_limit to be correct #1667

Merged
merged 7 commits into from
Jul 22, 2023
Merged
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
2 changes: 2 additions & 0 deletions .changelog/unreleased/bug-fixes/1667-faucet-limit-fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- Fix genesis `faucet_withdrawal_limit` parser to respect tokens' denomination.
([\#1667](https://github.com/anoma/namada/pull/1667))
10 changes: 6 additions & 4 deletions apps/src/lib/config/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use namada::types::key::dkg_session_keys::DkgPublicKey;
use namada::types::key::*;
use namada::types::time::{DateTimeUtc, DurationSecs};
use namada::types::token::Denomination;
use namada::types::uint::Uint;
use namada::types::{storage, token};

/// Genesis configuration file format
Expand All @@ -45,6 +46,7 @@ pub mod genesis_config {
use namada::types::key::*;
use namada::types::time::Rfc3339String;
use namada::types::token::Denomination;
use namada::types::uint::Uint;
use namada::types::{storage, token};
use serde::{Deserialize, Serialize};
use thiserror::Error;
Expand Down Expand Up @@ -122,8 +124,9 @@ pub mod genesis_config {
/// Testnet faucet PoW difficulty - defaults to `0` when not set
pub faucet_pow_difficulty: Option<testnet_pow::Difficulty>,
#[cfg(not(feature = "mainnet"))]
/// Testnet faucet withdrawal limit - defaults to 1000 NAM when not set
pub faucet_withdrawal_limit: Option<token::Amount>,
/// Testnet faucet withdrawal limit - defaults to 1000 tokens when not
/// set
pub faucet_withdrawal_limit: Option<Uint>,
// Initial validator set
pub validator: HashMap<String, ValidatorConfig>,
// Token accounts present at genesis
Expand Down Expand Up @@ -557,7 +560,6 @@ pub mod genesis_config {
.expect("Missing native token address"),
)
.expect("Invalid address");

let validators: HashMap<String, Validator> = validator
.iter()
.map(|(name, cfg)| (name.clone(), load_validator(cfg, &wasm)))
Expand Down Expand Up @@ -732,7 +734,7 @@ pub struct Genesis {
#[cfg(not(feature = "mainnet"))]
pub faucet_pow_difficulty: Option<testnet_pow::Difficulty>,
#[cfg(not(feature = "mainnet"))]
pub faucet_withdrawal_limit: Option<token::Amount>,
pub faucet_withdrawal_limit: Option<Uint>,
pub validators: Vec<Validator>,
pub token_accounts: Vec<TokenAccount>,
pub established_accounts: Vec<EstablishedAccount>,
Expand Down
9 changes: 6 additions & 3 deletions apps/src/lib/node/ledger/shell/init_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ where
fn initialize_established_accounts(
&mut self,
faucet_pow_difficulty: Option<testnet_pow::Difficulty>,
faucet_withdrawal_limit: Option<token::Amount>,
faucet_withdrawal_limit: Option<namada::types::uint::Uint>,
accounts: Vec<genesis::EstablishedAccount>,
implicit_vp_code_path: &str,
) -> Result<()> {
Expand Down Expand Up @@ -311,8 +311,11 @@ where
if vp_code_path == "vp_testnet_faucet.wasm" {
let difficulty = faucet_pow_difficulty.unwrap_or_default();
// withdrawal limit defaults to 1000 NAM when not set
let withdrawal_limit = faucet_withdrawal_limit
.unwrap_or_else(|| token::Amount::native_whole(1_000));
let withdrawal_limit =
faucet_withdrawal_limit.unwrap_or_else(|| {
token::Amount::native_whole(1_000).into()
});

testnet_pow::init_faucet_storage(
&mut self.wl_storage,
&address,
Expand Down
8 changes: 4 additions & 4 deletions core/src/ledger/testnet_pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ use crate::ledger::storage_api::collections::LazyMap;
use crate::types::address::Address;
use crate::types::hash::Hash;
use crate::types::storage::{self, DbKeySeg, Key};
use crate::types::token;
use crate::types::uint::Uint;

/// Initialize faucet's storage. This must be called at genesis if faucet
/// account is being used.
pub fn init_faucet_storage<S>(
storage: &mut S,
address: &Address,
difficulty: Difficulty,
withdrawal_limit: token::Amount,
withdrawal_limit: Uint,
) -> storage_api::Result<()>
where
S: StorageWrite,
Expand Down Expand Up @@ -457,7 +457,7 @@ where
pub fn read_withdrawal_limit<S>(
storage: &S,
address: &Address,
) -> storage_api::Result<token::Amount>
) -> storage_api::Result<Uint>
where
S: StorageRead,
{
Expand All @@ -471,7 +471,7 @@ where
pub fn write_withdrawal_limit<S>(
storage: &mut S,
address: &Address,
withdrawal_limit: token::Amount,
withdrawal_limit: Uint,
) -> Result<(), storage_api::Error>
where
S: StorageWrite,
Expand Down
70 changes: 67 additions & 3 deletions core/src/types/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use std::ops::{Add, AddAssign, BitAnd, Div, Mul, Neg, Rem, Sub, SubAssign};
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use impl_num_traits::impl_uint_num_traits;
use num_integer::Integer;
use serde::{Deserialize, Serialize};
use uint::construct_uint;

use crate::types::token;
Expand All @@ -31,8 +30,6 @@ construct_uint! {
/// Namada native type to replace for unsigned 256 bit
/// integers.
#[derive(
Serialize,
Deserialize,
BorshSerialize,
BorshDeserialize,
BorshSchema,
Expand All @@ -41,6 +38,62 @@ construct_uint! {
pub struct Uint(4);
}

impl serde::Serialize for Uint {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let amount_string = self.to_string();
serde::Serialize::serialize(&amount_string, serializer)
}
}

impl<'de> serde::Deserialize<'de> for Uint {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error as serdeError;
let amount_string: String =
serde::Deserialize::deserialize(deserializer)?;

let digits = amount_string
tzemanovic marked this conversation as resolved.
Show resolved Hide resolved
.chars()
.filter_map(|c| {
if c.is_ascii_digit() {
c.to_digit(10).map(Uint::from)
} else {
None
}
})
.rev()
.collect::<Vec<_>>();
if digits.len() != amount_string.len() {
return Err(D::Error::custom(AmountParseError::FromString));
}
if digits.len() > 77 {
return Err(D::Error::custom(AmountParseError::ScaleTooLarge(
digits.len() as u32,
77,
)));
}
let mut value = Uint::default();
let ten = Uint::from(10);
for (pow, digit) in digits.into_iter().enumerate() {
value = ten
.checked_pow(Uint::from(pow))
.and_then(|scaling| scaling.checked_mul(digit))
.and_then(|scaled| value.checked_add(scaled))
.ok_or(AmountParseError::PrecisionOverflow)
.map_err(D::Error::custom)?;
}
Ok(value)
}
}

impl_uint_num_traits!(Uint, 4);

impl Integer for Uint {
Expand Down Expand Up @@ -624,4 +677,15 @@ mod test_uint {
assert!(-that <= -this);
assert!(-that <= this);
}

#[test]
fn test_serialization_roundtrip() {
let amount: Uint = serde_json::from_str(r#""1000000000""#).unwrap();
assert_eq!(amount, Uint::from(1000000000));
let serialized = serde_json::to_string(&amount).unwrap();
assert_eq!(serialized, r#""1000000000""#);

let amount: Result<Uint, _> = serde_json::from_str(r#""1000000000.2""#);
assert!(amount.is_err());
}
}
2 changes: 1 addition & 1 deletion genesis/e2e-tests-single-node.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
genesis_time = "2021-09-30T10:00:00Z"
native_token = "NAM"
faucet_pow_difficulty = 1
faucet_withdrawal_limit = "1000000000"
faucet_withdrawal_limit = "1000"

[validator.validator-0]
# Validator's staked NAM at genesis.
Expand Down
2 changes: 1 addition & 1 deletion tests/src/storage_api/testnet_pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::vp;
fn test_challenge_and_solution() -> storage_api::Result<()> {
let faucet_address = address::testing::established_address_1();
let difficulty = Difficulty::try_new(1).unwrap();
let withdrawal_limit = token::Amount::native_whole(1_000);
let withdrawal_limit = token::Amount::native_whole(1_000).into();

let mut tx_env = TestTxEnv::default();

Expand Down
25 changes: 19 additions & 6 deletions wasm/wasm_source/src/vp_testnet_faucet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,25 @@ fn validate_tx(
}

for key in keys_changed.iter() {
let is_valid = if let Some([_, owner]) =
let is_valid = if let Some([token, owner]) =
token::is_any_token_balance_key(key)
{
if owner == &addr {
let pre: token::Amount = ctx.read_pre(key)?.unwrap_or_default();
let post: token::Amount =
ctx.read_post(key)?.unwrap_or_default();
let change = post.change() - pre.change();

let maybe_denom =
storage_api::token::read_denom(&ctx.pre(), token, None)?;
if maybe_denom.is_none() {
debug_log!(
"A denomination for token address {} does not exist \
in storage",
token,
);
return reject();
}
let denom = maybe_denom.unwrap();
if !change.non_negative() {
// Allow to withdraw without a sig if there's a valid PoW
if ctx.has_valid_pow() {
Expand All @@ -60,7 +70,10 @@ fn validate_tx(
&ctx.pre(),
&addr,
)?;
change >= -max_free_debit.change()

token::Amount::from_uint(change.abs(), 0).unwrap()
tzemanovic marked this conversation as resolved.
Show resolved Hide resolved
<= token::Amount::from_uint(max_free_debit, denom)
.unwrap()
} else {
debug_log!("No PoW solution, a signature is required");
// Debit without a solution has to signed
Expand Down Expand Up @@ -304,7 +317,7 @@ mod tests {
let vp_owner = address::testing::established_address_1();
let difficulty = testnet_pow::Difficulty::try_new(0).unwrap();
let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap();

let target = address::testing::established_address_2();
let token = address::nam();
Expand Down Expand Up @@ -349,7 +362,7 @@ mod tests {
let vp_owner = address::testing::established_address_1();
let difficulty = testnet_pow::Difficulty::try_new(0).unwrap();
let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap();

let target = address::testing::established_address_2();
let target_key = key::testing::keypair_1();
Expand Down Expand Up @@ -414,7 +427,7 @@ mod tests {
// Init the VP
let difficulty = testnet_pow::Difficulty::try_new(0).unwrap();
let withdrawal_limit = token::Amount::from_uint(MAX_FREE_DEBIT as u64, 0).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit).unwrap();
testnet_pow::init_faucet_storage(&mut tx_env.wl_storage, &vp_owner, difficulty, withdrawal_limit.into()).unwrap();

let keypair = key::testing::keypair_1();
let public_key = &keypair.ref_to();
Expand Down