Skip to content

Commit

Permalink
Merge branch 'bengt/faucet-0.18-fix' (#1667)
Browse files Browse the repository at this point in the history
* origin/bengt/faucet-0.18-fix:
  changelog: add #1667
  [fix]: Fixing errors introduced by merging in main
  [chore]: Incorporating in review comments
  Update core/src/types/uint.rs
  Update apps/src/lib/config/genesis.rs
  [fix]: Fixed the faucet to use uint instead of amount. This makes it portable across different assets
  converts faucet_withdrawal_limit to be correct
  • Loading branch information
Fraccaman committed Jul 21, 2023
2 parents 7a18295 + 5ca0eab commit 9381168
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 22 deletions.
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
.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()
<= 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

0 comments on commit 9381168

Please sign in to comment.