From 291413d33052b210da0b24d2c94a922e34ad0a30 Mon Sep 17 00:00:00 2001
From: Daksh <41485688+Daksh14@users.noreply.github.com>
Date: Tue, 10 Sep 2024 20:06:29 -0400
Subject: [PATCH 1/3] rusk-wallet: Add phoenix/moonlight conversion support
- Increment nonce everywhere since wallet-core doesn't do it anymore
- Fix wrong convert limit
---
rusk-wallet/src/bin/command.rs | 70 ++++++++++++++++++++++++
rusk-wallet/src/bin/interactive.rs | 39 +++++++++++--
rusk-wallet/src/wallet.rs | 88 ++++++++++++++++++++++++++----
3 files changed, 181 insertions(+), 16 deletions(-)
diff --git a/rusk-wallet/src/bin/command.rs b/rusk-wallet/src/bin/command.rs
index 1c8e39d2d2..b56860b319 100644
--- a/rusk-wallet/src/bin/command.rs
+++ b/rusk-wallet/src/bin/command.rs
@@ -188,6 +188,42 @@ pub(crate) enum Command {
gas_price: Lux,
},
+ /// Convert Phoenix balance to moonlight for the same owned address
+ PhoenixToMoonlight {
+ /// Bls or Phoenix Address from which to convert DUSK to
+ addr: Option
,
+
+ /// Amount of DUSK to transfer to moonlight account
+ #[clap(short, long)]
+ amt: Dusk,
+
+ /// Max amt of gas for this transaction
+ #[clap(short = 'l', long, default_value_t= DEFAULT_STAKE_GAS_LIMIT)]
+ gas_limit: u64,
+
+ /// Price you're going to pay for each gas unit (in LUX)
+ #[clap(short = 'p', long, default_value_t= DEFAULT_PRICE)]
+ gas_price: Lux,
+ },
+
+ /// Convert moonlight balance to phoenix for the same owned address
+ MoonlightToPhoenix {
+ /// Bls or Phoenix Address from which to convert DUSK to
+ addr: Option,
+
+ /// Amount of DUSK to transfer to phoenix account
+ #[clap(short, long)]
+ amt: Dusk,
+
+ /// Max amt of gas for this transaction
+ #[clap(short = 'l', long, default_value_t= DEFAULT_STAKE_GAS_LIMIT)]
+ gas_limit: u64,
+
+ /// Price you're going to pay for each gas unit (in LUX)
+ #[clap(short = 'p', long, default_value_t= DEFAULT_PRICE)]
+ gas_price: Lux,
+ },
+
/// Export BLS provisioner key pair
Export {
/// Address for which you want the exported keys [default: first
@@ -385,6 +421,40 @@ impl Command {
Ok(RunResult::PhoenixHistory(transactions))
}
+ Command::PhoenixToMoonlight {
+ addr,
+ gas_limit,
+ gas_price,
+ amt,
+ } => {
+ wallet.sync().await?;
+ let addr = match addr {
+ Some(addr) => wallet.claim_as_address(addr)?,
+ None => wallet.default_address(),
+ };
+
+ let gas = Gas::new(gas_limit).with_price(gas_price);
+
+ let tx = wallet.phoenix_to_moonlight(addr, amt, gas).await?;
+ Ok(RunResult::Tx(tx.hash()))
+ }
+ Command::MoonlightToPhoenix {
+ addr,
+ amt,
+ gas_limit,
+ gas_price,
+ } => {
+ wallet.sync().await?;
+ let addr = match addr {
+ Some(addr) => wallet.claim_as_address(addr)?,
+ None => wallet.default_address(),
+ };
+
+ let gas = Gas::new(gas_limit).with_price(gas_price);
+
+ let tx = wallet.moonlight_to_phoenix(addr, amt, gas).await?;
+ Ok(RunResult::Tx(tx.hash()))
+ }
Command::Create { .. } => Ok(RunResult::Create()),
Command::Restore { .. } => Ok(RunResult::Restore()),
Command::Settings => Ok(RunResult::Settings()),
diff --git a/rusk-wallet/src/bin/interactive.rs b/rusk-wallet/src/bin/interactive.rs
index a6befe3ab0..f5894bdd04 100644
--- a/rusk-wallet/src/bin/interactive.rs
+++ b/rusk-wallet/src/bin/interactive.rs
@@ -96,7 +96,9 @@ pub(crate) async fn run_loop(
// request operation to perform
let op = match wallet.is_online().await {
- true => menu_op(addr.clone(), spendable, settings),
+ true => {
+ menu_op(addr.clone(), spendable, moonlight_bal, settings)
+ }
false => menu_op_offline(addr.clone(), settings),
};
@@ -210,6 +212,8 @@ enum CommandMenuItem {
PhoenixTransfer,
MoonlightTransfer,
PhoenixStake,
+ PhoenixToMoonlight,
+ MoonlightToPhoenix,
StakeInfo,
PhoenixUnstake,
PhoenixWithdraw,
@@ -221,7 +225,8 @@ enum CommandMenuItem {
/// selected address
fn menu_op(
addr: Address,
- balance: Dusk,
+ phoenix_balance: Dusk,
+ moonlight_balance: Dusk,
settings: &Settings,
) -> anyhow::Result {
use CommandMenuItem as CMI;
@@ -234,6 +239,14 @@ fn menu_op(
.add(CMI::StakeInfo, "Check existing stake")
.add(CMI::PhoenixUnstake, "Phoenix Unstake Dusk")
.add(CMI::PhoenixWithdraw, "Phoenix Withdraw staking reward")
+ .add(
+ CMI::PhoenixToMoonlight,
+ "Convert dusk from phoenix account to moonlight",
+ )
+ .add(
+ CMI::MoonlightToPhoenix,
+ "Convert dusk from moonlight account to phoenix",
+ )
.add(CMI::Export, "Export provisioner key-pair")
.separator()
.add(CMI::Back, "Back");
@@ -254,7 +267,7 @@ fn menu_op(
AddrOp::Run(Box::new(Command::PhoenixTransfer {
sndr: Some(addr),
rcvr: prompt::request_rcvr_addr("recipient")?,
- amt: prompt::request_token_amt("transfer", balance)?,
+ amt: prompt::request_token_amt("transfer", phoenix_balance)?,
gas_limit: prompt::request_gas_limit(gas::DEFAULT_LIMIT)?,
gas_price: prompt::request_gas_price()?,
}))
@@ -263,14 +276,14 @@ fn menu_op(
AddrOp::Run(Box::new(Command::MoonlightTransfer {
sndr: Some(addr),
rcvr: prompt::request_rcvr_addr("recipient")?,
- amt: prompt::request_token_amt("transfer", balance)?,
+ amt: prompt::request_token_amt("transfer", moonlight_balance)?,
gas_limit: prompt::request_gas_limit(gas::DEFAULT_LIMIT)?,
gas_price: prompt::request_gas_price()?,
}))
}
CMI::PhoenixStake => AddrOp::Run(Box::new(Command::PhoenixStake {
addr: Some(addr),
- amt: prompt::request_token_amt("stake", balance)?,
+ amt: prompt::request_token_amt("stake", phoenix_balance)?,
gas_limit: prompt::request_gas_limit(DEFAULT_STAKE_GAS_LIMIT)?,
gas_price: prompt::request_gas_price()?,
})),
@@ -290,6 +303,22 @@ fn menu_op(
gas_price: prompt::request_gas_price()?,
}))
}
+ CMI::MoonlightToPhoenix => {
+ AddrOp::Run(Box::new(Command::MoonlightToPhoenix {
+ addr: Some(addr),
+ amt: prompt::request_token_amt("convert", moonlight_balance)?,
+ gas_limit: prompt::request_gas_limit(gas::DEFAULT_LIMIT)?,
+ gas_price: prompt::request_gas_price()?,
+ }))
+ }
+ CMI::PhoenixToMoonlight => {
+ AddrOp::Run(Box::new(Command::PhoenixToMoonlight {
+ addr: Some(addr),
+ amt: prompt::request_token_amt("convert", phoenix_balance)?,
+ gas_limit: prompt::request_gas_limit(gas::DEFAULT_LIMIT)?,
+ gas_price: prompt::request_gas_price()?,
+ }))
+ }
CMI::Export => AddrOp::Run(Box::new(Command::Export {
addr: Some(addr),
name: None,
diff --git a/rusk-wallet/src/wallet.rs b/rusk-wallet/src/wallet.rs
index f41dfa62fe..42622fdf95 100644
--- a/rusk-wallet/src/wallet.rs
+++ b/rusk-wallet/src/wallet.rs
@@ -28,8 +28,9 @@ use wallet_core::{
derive_phoenix_vk,
},
transaction::{
- moonlight, moonlight_stake, moonlight_unstake, phoenix, phoenix_stake,
- phoenix_stake_reward, phoenix_unstake,
+ moonlight, moonlight_stake, moonlight_to_phoenix, moonlight_unstake,
+ phoenix, phoenix_stake, phoenix_stake_reward, phoenix_to_moonlight,
+ phoenix_unstake,
},
BalanceInfo,
};
@@ -631,12 +632,15 @@ impl Wallet {
return Err(Error::NotEnoughGas);
}
- let mut from_sk = self.bls_secret_key(sender.index()?);
+ let sender = sender.index()?;
+
+ let mut from_sk = self.bls_secret_key(sender);
let apk = rcvr.apk()?;
+ let from_pk = self.bls_public_key(sender);
let amt = *amt;
let state = self.state()?;
- let account = state.fetch_account(apk)?;
+ let nonce = state.fetch_account(&from_pk)?.nonce + 1;
let chain_id = state.fetch_chain_id()?;
let tx = moonlight(
@@ -646,7 +650,7 @@ impl Wallet {
0,
gas.limit,
gas.price,
- account.nonce,
+ nonce,
chain_id,
None::,
)?;
@@ -687,7 +691,8 @@ impl Wallet {
let nonce = state
.fetch_stake(&AccountPublicKey::from(&stake_sk))?
.map(|s| s.nonce)
- .unwrap_or(0);
+ .unwrap_or(0)
+ + 1;
let inputs = state
.inputs(sender_index, amt + gas.limit * gas.price)?
@@ -734,10 +739,10 @@ impl Wallet {
let sender_index = addr.index()?;
let mut stake_sk = self.bls_secret_key(sender_index);
let pk = AccountPublicKey::from(&stake_sk);
- let account = state.fetch_account(&pk)?;
let chain_id = state.fetch_chain_id()?;
+ let moonlight_current_nonce = state.fetch_account(&pk)?.nonce + 1;
- let nonce = state.fetch_stake(&pk)?.map(|s| s.nonce).unwrap_or(0);
+ let nonce = state.fetch_stake(&pk)?.map(|s| s.nonce + 1).unwrap_or(0);
let stake = moonlight_stake(
&stake_sk,
@@ -745,7 +750,7 @@ impl Wallet {
amt,
gas.limit,
gas.price,
- account.nonce,
+ moonlight_current_nonce,
nonce,
chain_id,
)?;
@@ -831,7 +836,7 @@ impl Wallet {
let pk = AccountPublicKey::from(&stake_sk);
let chain_id = state.fetch_chain_id()?;
- let account = state.fetch_account(&pk)?;
+ let account_nonce = state.fetch_account(&pk)?.nonce + 1;
let unstake_value = state
.fetch_stake(&pk)?
@@ -846,7 +851,7 @@ impl Wallet {
unstake_value,
gas.price,
gas.limit,
- account.nonce + 1,
+ account_nonce,
chain_id,
)?;
@@ -902,6 +907,67 @@ impl Wallet {
state.prove_and_propagate(withdraw)
}
+ /// Convert balance from phoenix to moonlight
+ pub async fn phoenix_to_moonlight(
+ &self,
+ sender_addr: &Address,
+ amt: Dusk,
+ gas: Gas,
+ ) -> Result {
+ let mut rng = StdRng::from_entropy();
+ let state = self.state()?;
+ let sender_index = sender_addr.index()?;
+ let amt = *amt;
+ let inputs = state.inputs(sender_index, amt + gas.limit * gas.price)?;
+
+ let root = state.fetch_root()?;
+ let chain_id = state.fetch_chain_id()?;
+
+ let mut sender_sk = self.phoenix_secret_key(sender_index);
+ let mut stake_sk = self.bls_secret_key(sender_index);
+
+ let convert = phoenix_to_moonlight(
+ &mut rng, &sender_sk, &stake_sk, inputs, root, amt, gas.limit,
+ gas.price, chain_id, &Prover,
+ )?;
+
+ sender_sk.zeroize();
+ stake_sk.zeroize();
+
+ state.prove_and_propagate(convert)
+ }
+
+ /// Convert balance from moonlight to phoenix
+ pub async fn moonlight_to_phoenix(
+ &self,
+ sender_addr: &Address,
+ amt: Dusk,
+ gas: Gas,
+ ) -> Result {
+ let mut rng = StdRng::from_entropy();
+ let state = self.state()?;
+ let sender_index = sender_addr.index()?;
+ let amt = *amt;
+
+ let pk = self.bls_public_key(sender_index);
+
+ let nonce = state.fetch_account(&pk)?.nonce + 1;
+ let chain_id = state.fetch_chain_id()?;
+
+ let mut sender_sk = self.phoenix_secret_key(sender_index);
+ let mut stake_sk = self.bls_secret_key(sender_index);
+
+ let convert = moonlight_to_phoenix(
+ &mut rng, &stake_sk, &sender_sk, amt, gas.limit, gas.price, nonce,
+ chain_id,
+ )?;
+
+ sender_sk.zeroize();
+ stake_sk.zeroize();
+
+ state.prove_and_propagate(convert)
+ }
+
/// Returns bls key pair for provisioner nodes
pub fn provisioner_keys(
&self,
From 55dbf97d9cfdf563c407dce47d567cf0d4ee72fa Mon Sep 17 00:00:00 2001
From: Daksh <41485688+Daksh14@users.noreply.github.com>
Date: Tue, 10 Sep 2024 20:06:43 -0400
Subject: [PATCH 2/3] wallet-core: Do NOT increment nonce everywhere
---
wallet-core/src/transaction.rs | 58 +++++++++++++++++++++++-----------
1 file changed, 40 insertions(+), 18 deletions(-)
diff --git a/wallet-core/src/transaction.rs b/wallet-core/src/transaction.rs
index 51f9a313c0..6f449ccd71 100644
--- a/wallet-core/src/transaction.rs
+++ b/wallet-core/src/transaction.rs
@@ -87,6 +87,11 @@ pub fn phoenix(
/// Creates a totally generic Moonlight [`Transaction`], all fields being
/// variable.
///
+/// # Note
+/// The `current_nonce` is NOT incremented and should be incremented
+/// by the caller of this function, if its not done so, rusk
+/// will throw 500 error
+///
/// # Errors
/// The creation of a transaction is not possible and will error if:
/// - the Memo provided with `data` is too large
@@ -118,6 +123,11 @@ pub fn moonlight(
/// Create a [`Transaction`] to stake from phoenix-notes.
///
+/// # Note
+/// The `current_nonce` is NOT incremented and should be incremented
+/// by the caller of this function, if its not done so, rusk
+/// will throw 500 error
+///
/// # Errors
/// The creation of a transaction is not possible and will error if:
/// - one of the input-notes doesn't belong to the `phoenix_sender_sk`
@@ -145,9 +155,8 @@ pub fn phoenix_stake(
let transfer_value = 0;
let obfuscated_transaction = false;
let deposit = stake_value;
- let nonce = current_nonce + 1;
- let stake = Stake::new(stake_sk, stake_value, nonce, chain_id);
+ let stake = Stake::new(stake_sk, stake_value, current_nonce, chain_id);
let contract_call = ContractCall::new(STAKE_CONTRACT, "stake", &stake)?;
@@ -171,6 +180,11 @@ pub fn phoenix_stake(
/// Create a [`Transaction`] to stake from a Moonlight account.
///
+/// # Note
+/// The `moonlight_current_nonce` and `stake_current_nonce` are NOT incremented
+/// and should be incremented by the caller of this function, if its not done
+/// so, rusk will throw 500 error
+///
/// # Errors
/// The creation of this transaction doesn't error, but still returns a result
/// for the sake of API consistency.
@@ -187,10 +201,9 @@ pub fn moonlight_stake(
) -> Result {
let transfer_value = 0;
let deposit = stake_value;
- let moonlight_nonce = moonlight_current_nonce + 1;
- let stake_nonce = stake_current_nonce + 1;
- let stake = Stake::new(stake_sk, stake_value, stake_nonce, chain_id);
+ let stake =
+ Stake::new(stake_sk, stake_value, stake_current_nonce, chain_id);
let contract_call = ContractCall::new(STAKE_CONTRACT, "stake", &stake)?;
@@ -201,7 +214,7 @@ pub fn moonlight_stake(
deposit,
gas_limit,
gas_price,
- moonlight_nonce,
+ moonlight_current_nonce,
chain_id,
Some(contract_call),
)
@@ -277,6 +290,10 @@ pub fn phoenix_stake_reward(
/// Create a [`Transaction`] to withdraw stake rewards into Moonlight account.
///
+/// # Note
+/// The `current_nonce` is NOT incremented and should be incremented by the
+/// caller of this function, if its not done so, rusk will throw 500 error
+///
/// # Errors
/// The creation of this transaction doesn't error, but still returns a result
/// for the sake of API consistency.
@@ -293,9 +310,8 @@ pub fn moonlight_stake_reward(
) -> Result {
let transfer_value = 0;
let deposit = 0;
- let nonce = current_nonce + 1;
- let gas_payment_token = WithdrawReplayToken::Moonlight(nonce);
+ let gas_payment_token = WithdrawReplayToken::Moonlight(current_nonce);
let contract_call = stake_reward_to_moonlight(
rng,
@@ -312,7 +328,7 @@ pub fn moonlight_stake_reward(
deposit,
gas_limit,
gas_price,
- nonce,
+ current_nonce,
chain_id,
Some(contract_call),
)
@@ -387,6 +403,10 @@ pub fn phoenix_unstake(
/// Create a [`Transaction`] to unstake into a Moonlight account.
///
+/// # Note
+/// The `current_nonce` is NOT incremented and should be incremented by the
+/// caller of this function, if its not done so, rusk will throw 500 error
+///
/// # Errors
/// The creation of a transaction is not possible and will error if:
/// - the Memo provided with `data` is too large
@@ -403,9 +423,8 @@ pub fn moonlight_unstake(
) -> Result {
let transfer_value = 0;
let deposit = 0;
- let nonce = current_nonce + 1;
- let gas_payment_token = WithdrawReplayToken::Moonlight(nonce);
+ let gas_payment_token = WithdrawReplayToken::Moonlight(current_nonce);
let contract_call = unstake_to_moonlight(
rng,
@@ -422,7 +441,7 @@ pub fn moonlight_unstake(
deposit,
gas_limit,
gas_price,
- nonce,
+ current_nonce,
chain_id,
Some(contract_call),
)
@@ -431,7 +450,8 @@ pub fn moonlight_unstake(
/// Create an unproven [`Transaction`] to convert Phoenix Dusk into Moonlight
/// Dusk.
///
-/// Note that ownership of both sender and receiver keys is required, and
+/// # Note
+/// The ownership of both sender and receiver keys is required, and
/// enforced by the protocol.
///
/// # Errors
@@ -501,8 +521,12 @@ pub fn phoenix_to_moonlight(
/// Create a [`Transaction`] to convert Moonlight Dusk into Phoenix Dusk.
///
-/// Note that ownership of both sender and receiver keys is required, and
+/// # Note
+/// 1. The ownership of both sender and receiver keys is required, and
/// enforced by the protocol.
+/// 2. `current_nonce` is NOT incremented and should be incremented by the
+/// caller
+/// of this function, if its not done so, rusk will throw 500 error
///
/// # Errors
/// The creation of this transaction doesn't error, but still returns a result
@@ -521,9 +545,7 @@ pub fn moonlight_to_phoenix(
let transfer_value = 0;
let deposit = convert_value; // a convertion is a simultaneous deposit to *and* withdrawal from the
// transfer contract
- let nonce = current_nonce + 1;
-
- let gas_payment_token = WithdrawReplayToken::Moonlight(nonce);
+ let gas_payment_token = WithdrawReplayToken::Moonlight(current_nonce);
let contract_call = convert_to_phoenix(
rng,
@@ -539,7 +561,7 @@ pub fn moonlight_to_phoenix(
deposit,
gas_limit,
gas_price,
- nonce,
+ current_nonce,
chain_id,
Some(contract_call),
)
From be1de840b6aa6439c7a479976f62627ea1bdaa7a Mon Sep 17 00:00:00 2001
From: Daksh <41485688+Daksh14@users.noreply.github.com>
Date: Tue, 10 Sep 2024 20:06:56 -0400
Subject: [PATCH 3/3] test-wallet: Increment nonce where needed
---
test-wallet/src/imp.rs | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/test-wallet/src/imp.rs b/test-wallet/src/imp.rs
index 7340ef3826..ecdf874d04 100644
--- a/test-wallet/src/imp.rs
+++ b/test-wallet/src/imp.rs
@@ -471,7 +471,8 @@ where
.state
.fetch_stake(&stake_pk)
.map_err(Error::from_state_err)?
- .nonce;
+ .nonce
+ + 1;
let chain_id =
self.state.fetch_chain_id().map_err(Error::from_state_err)?;
@@ -746,8 +747,8 @@ where
stake_value,
gas_limit,
gas_price,
- sender_account.nonce,
- staker_data.nonce,
+ sender_account.nonce + 1,
+ staker_data.nonce + 1,
chain_id,
)?;
@@ -798,7 +799,7 @@ where
unstake_value,
gas_limit,
gas_price,
- sender_account.nonce,
+ sender_account.nonce + 1,
chain_id,
)?;
@@ -843,7 +844,7 @@ where
staker_data.reward,
gas_limit,
gas_price,
- sender_account.nonce,
+ sender_account.nonce + 1,
chain_id,
)?;
@@ -875,7 +876,7 @@ where
.fetch_account(&moonlight_sender_pk)
.map_err(Error::from_state_err)?;
- let nonce = moonlight_sender_account.nonce;
+ let nonce = moonlight_sender_account.nonce + 1;
let chain_id =
self.state.fetch_chain_id().map_err(Error::from_state_err)?;