Skip to content

Commit

Permalink
vp_testnet_faucet: R/W withdrawal limit from storage, add to genesis
Browse files Browse the repository at this point in the history
  • Loading branch information
tzemanovic committed Dec 28, 2022
1 parent cd8b45e commit c7c2b7d
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 11 deletions.
6 changes: 6 additions & 0 deletions apps/src/lib/config/genesis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub mod genesis_config {
pub native_token: String,
/// Faucet PoW difficulty - defaults to `0` when not set
pub faucet_pow_difficulty: Option<faucet_pow::Difficulty>,
/// Faucet withdrawal limit - defaults to 1000 NAM when not set
pub faucet_withdrawal_limit: Option<token::Amount>,
// Initial validator set
pub validator: HashMap<String, ValidatorConfig>,
// Token accounts present at genesis
Expand Down Expand Up @@ -500,6 +502,7 @@ pub mod genesis_config {
genesis_time,
native_token,
faucet_pow_difficulty,
faucet_withdrawal_limit,
validator,
token,
established,
Expand Down Expand Up @@ -636,6 +639,7 @@ pub mod genesis_config {
genesis_time: genesis_time.try_into().unwrap(),
native_token,
faucet_pow_difficulty,
faucet_withdrawal_limit,
validators: validators.into_values().collect(),
token_accounts,
established_accounts: established_accounts.into_values().collect(),
Expand Down Expand Up @@ -685,6 +689,7 @@ pub struct Genesis {
pub genesis_time: DateTimeUtc,
pub native_token: Address,
pub faucet_pow_difficulty: Option<faucet_pow::Difficulty>,
pub faucet_withdrawal_limit: Option<token::Amount>,
pub validators: Vec<Validator>,
pub token_accounts: Vec<TokenAccount>,
pub established_accounts: Vec<EstablishedAccount>,
Expand Down Expand Up @@ -960,6 +965,7 @@ pub fn genesis() -> Genesis {
gov_params: GovParams::default(),
native_token: address::nam(),
faucet_pow_difficulty: None,
faucet_withdrawal_limit: None,
}
}

Expand Down
5 changes: 5 additions & 0 deletions apps/src/lib/node/ledger/shell/init_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,15 @@ where
if vp_code_path == "vp_testnet_faucet.wasm" {
let difficulty =
genesis.faucet_pow_difficulty.unwrap_or_default();
// withdrawal limit defaults to 1000 NAM when not set
let withdrawal_limit = genesis
.faucet_withdrawal_limit
.unwrap_or_else(|| token::Amount::whole(1_000));
faucet_pow::init_faucet_storage(
&mut self.storage,
&address,
difficulty,
withdrawal_limit,
)
.expect("Couldn't init faucet storage")
}
Expand Down
64 changes: 62 additions & 2 deletions core/src/ledger/faucet_pow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ pub fn init_faucet_storage<S>(
storage: &mut S,
address: &Address,
difficulty: Difficulty,
withdrawal_limit: token::Amount,
) -> storage_api::Result<()>
where
S: StorageWrite,
{
write_difficulty(storage, address, difficulty)
write_difficulty(storage, address, difficulty)?;
write_withdrawal_limit(storage, address, withdrawal_limit)
}

/// Counters are associated with transfer target addresses.
Expand Down Expand Up @@ -262,6 +264,8 @@ pub struct Keys {
counters: &'static str,
/// PoW difficulty
difficulty: &'static str,
/// withdrawal limit
withdrawal_limit: &'static str,
}

/// Storage key prefix to the `counters` field. The rest of the key is composed
Expand Down Expand Up @@ -317,6 +321,30 @@ pub fn is_difficulty_key(key: &storage::Key, faucet_address: &Address) -> bool {
)
}

/// Storage key to the `withdrawal_limit` field.
pub fn withdrawal_limit_key(address: &Address) -> storage::Key {
storage::Key {
segments: vec![
DbKeySeg::AddressSeg(address.clone()),
DbKeySeg::StringSeg(Keys::VALUES.withdrawal_limit.to_string()),
],
}
}

/// Is the storage key for the `withdrawal_limit` field?
pub fn is_withdrawal_limit_key(
key: &storage::Key,
faucet_address: &Address,
) -> bool {
matches!(
&key.segments[..],
[
DbKeySeg::AddressSeg(address),
DbKeySeg::StringSeg(sub_key),
] if address == faucet_address && sub_key.as_str() == Keys::VALUES.withdrawal_limit,
)
}

/// Read faucet's counter value for a given target address.
pub fn get_counter<S>(
storage: &S,
Expand Down Expand Up @@ -412,6 +440,32 @@ where
storage.write(&difficulty_key(address), difficulty)
}

/// Read the withdrawal limit.
pub fn read_withdrawal_limit<S>(
storage: &S,
address: &Address,
) -> storage_api::Result<token::Amount>
where
S: StorageRead,
{
let withdrawal_limit = storage
.read(&withdrawal_limit_key(address))?
.expect("withdrawal_limit must always be set");
Ok(withdrawal_limit)
}

/// Write faucet withdrawal limit
pub fn write_withdrawal_limit<S>(
storage: &mut S,
address: &Address,
withdrawal_limit: token::Amount,
) -> Result<(), storage_api::Error>
where
S: StorageWrite,
{
storage.write(&withdrawal_limit_key(address), withdrawal_limit)
}

#[cfg(test)]
mod test {
use super::*;
Expand Down Expand Up @@ -444,6 +498,7 @@ mod test_with_tx_and_vp_env {
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::whole(1_000);

let mut tx_env = TestTxEnv::default();

Expand All @@ -454,7 +509,12 @@ mod test_with_tx_and_vp_env {
// Ensure that the addresses exists, so we can use them in a tx
tx_env.spawn_accounts([&faucet_address, &target, &token]);

init_faucet_storage(&mut tx_env.storage, &faucet_address, difficulty)?;
init_faucet_storage(
&mut tx_env.storage,
&faucet_address,
difficulty,
withdrawal_limit,
)?;

let transfer = token::Transfer {
source: faucet_address.clone(),
Expand Down
1 change: 1 addition & 0 deletions genesis/e2e-tests-single-node.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
genesis_time = "2021-09-30T10:00:00Z"
native_token = "NAM"
faucet_pow_difficulty = 1
faucet_withdrawal_limit = 1_000

[validator.validator-0]
# Validator's staked NAM at genesis.
Expand Down
27 changes: 18 additions & 9 deletions wasm/wasm_source/src/vp_testnet_faucet.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
//! A "faucet" account for testnet.
//!
//! This VP allows anyone to withdraw up to [`MAX_FREE_DEBIT`] tokens without
//! the faucet's signature.
//! This VP allows anyone to withdraw up to
//! [`faucet_pow::read_withdrawal_limit`] tokens without the faucet's signature,
//! but with a valid PoW challenge solution that cannot be replayed.
//!
//! Any other storage key changes are allowed only with a valid signature.
use namada_vp_prelude::*;
use once_cell::unsync::Lazy;

/// Allows anyone to withdraw up to 1_000 tokens in a single tx
pub const MAX_FREE_DEBIT: i128 = 1_000_000_000; // in micro units

#[validity_predicate]
fn validate_tx(
ctx: &Ctx,
Expand Down Expand Up @@ -81,7 +79,12 @@ fn validate_tx(
Some(solution) => {
// Validate it
if solution.validate(ctx, &addr)? {
change >= -MAX_FREE_DEBIT
let max_free_debit =
faucet_pow::read_withdrawal_limit(
&ctx.pre(),
&addr,
)?;
change >= -max_free_debit.change()
} else {
debug_log!("Invalid PoW solution");
false
Expand Down Expand Up @@ -198,6 +201,9 @@ mod tests {
const VP_ALWAYS_TRUE_WASM: &str =
"../../wasm_for_tests/vp_always_true.wasm";

/// Allows anyone to withdraw up to 1_000 tokens in a single tx
pub const MAX_FREE_DEBIT: i128 = 1_000_000_000; // in micro units

/// Test that no-op transaction (i.e. no storage modifications) accepted.
#[test]
fn test_no_op_transaction() {
Expand Down Expand Up @@ -361,7 +367,8 @@ mod tests {
// Init the VP
let vp_owner = address::testing::established_address_1();
let difficulty = faucet_pow::Difficulty::try_new(0).unwrap();
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty).unwrap();
let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64);
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap();

let target = address::testing::established_address_2();
let token = address::nam();
Expand Down Expand Up @@ -399,7 +406,8 @@ mod tests {
// Init the VP
let vp_owner = address::testing::established_address_1();
let difficulty = faucet_pow::Difficulty::try_new(0).unwrap();
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty).unwrap();
let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64);
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap();

let target = address::testing::established_address_2();
let target_key = key::testing::keypair_1();
Expand Down Expand Up @@ -464,7 +472,8 @@ mod tests {

// Init the VP
let difficulty = faucet_pow::Difficulty::try_new(0).unwrap();
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty).unwrap();
let withdrawal_limit = token::Amount::from(MAX_FREE_DEBIT as u64);
faucet_pow::init_faucet_storage(&mut tx_env.storage, &vp_owner, difficulty, withdrawal_limit).unwrap();

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

0 comments on commit c7c2b7d

Please sign in to comment.