Skip to content

Commit

Permalink
rusk-wallet: Adjust rusk-wallet so that rusk can use it
Browse files Browse the repository at this point in the history
This change includes:
- add moonlight support
- add functions that return a key for a given index
- add code that can generate a test-wallet with a seed of [0u8;
  RNG_SEED]
  • Loading branch information
moCello committed Sep 6, 2024
1 parent 65e11e5 commit 7df7df7
Show file tree
Hide file tree
Showing 3 changed files with 199 additions and 34 deletions.
24 changes: 23 additions & 1 deletion rusk-wallet/src/clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ mod sync;

use dusk_bytes::Serializable;
use execution_core::{
transfer::{phoenix::Prove, Transaction},
signatures::bls::PublicKey as AccountPublicKey,
transfer::{moonlight::AccountData, phoenix::Prove, Transaction},
Error as ExecutionCoreError,
};
use flume::Receiver;
Expand Down Expand Up @@ -215,6 +216,27 @@ impl State {
inputs
}

pub(crate) fn fetch_account(
&self,
pk: &AccountPublicKey,
) -> Result<AccountData, Error> {
let status = self.status;
status("Fetching account-data...");

let account = self
.client
.contract_query::<_, 1024>(TRANSFER_CONTRACT, "account", pk)
.wait()?;
let account = rkyv::from_bytes(&account).map_err(|_| Error::Rkyv)?;
status("account-data received!");

let account_address = pk.to_bytes().to_vec();
let account_address = bs58::encode(account_address).into_string();
println!("Account address: {}", account_address);

Ok(account)
}

pub(crate) fn fetch_notes(
&self,
pk: &PhoenixPublicKey,
Expand Down
134 changes: 101 additions & 33 deletions rusk-wallet/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ use std::path::{Path, PathBuf};
use wallet_core::{
phoenix_balance,
prelude::keys::{
derive_bls_sk, derive_phoenix_pk, derive_phoenix_sk, derive_phoenix_vk,
derive_bls_pk, derive_bls_sk, derive_phoenix_pk, derive_phoenix_sk,
derive_phoenix_vk,
},
transaction::{
phoenix, phoenix_stake, phoenix_stake_reward, phoenix_unstake,
moonlight, phoenix, phoenix_stake, phoenix_stake_reward,
phoenix_unstake,
},
BalanceInfo,
};
Expand Down Expand Up @@ -356,20 +358,13 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
}

let index = addr.index()?;
let notes: Vec<NoteLeaf> = state
.fetch_notes(addr.pk())?
.into_iter()
.map(|data| NoteLeaf {
note: data.note,
block_height: data.height,
})
.collect();
let notes = state.fetch_notes(addr.pk())?;

let seed = self.store.get_seed();

Ok(phoenix_balance(
&derive_phoenix_vk(seed, index),
notes.into_iter(),
notes.iter(),
))
}

Expand All @@ -395,14 +390,85 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
&self.addresses
}

/// Executes a generic contract call
pub async fn execute(
/// Returns the phoenix public-key for a given index
pub fn phoenix_public_key(&self, index: u8) -> PhoenixPublicKey {
let seed = self.store.get_seed();
derive_phoenix_pk(&seed, index)
}

/// Returns the bls public-key for a given index
pub fn account_public_key(&self, index: u8) -> BlsPublicKey {
let seed = self.store.get_seed();
derive_bls_pk(&seed, index)
}

/// Returns the bls secret-key for a given index
pub fn account_secret_key(&self, index: u8) -> BlsSecretKey {
let seed = self.store.get_seed();
derive_bls_sk(&seed, index)
}

/// Creates a generic moonlight transaction.
#[allow(clippy::too_many_arguments)]
pub fn moonlight_transaction(
&self,
from_addr: &Address,
to_account: Option<BlsPublicKey>,
transfer_value: Dusk,
deposit: Dusk,
gas: Gas,
exec: Option<impl Into<TransactionData>>,
) -> Result<Transaction, Error> {
// make sure we own the sender address
if !from_addr.is_owned() {
return Err(Error::Unauthorized);
}

// check gas limits
if !gas.is_enough() {
return Err(Error::NotEnoughGas);
}

let seed = self.store.get_seed();

let state = self.state()?;
let deposit = *deposit;

let from_index = from_addr.index()?;
let mut from_sk = derive_bls_sk(&seed, from_index);
let from_account = BlsPublicKey::from(&from_sk);

let account = state.fetch_account(&from_account)?;

// technically this check is not necessary, but it's nice to not spam
// the network with transactions that are unspendable.
let nonce = account.nonce + 1;

let chain_id = state.fetch_chain_id()?;

let tx = moonlight(
&from_sk,
to_account,
*transfer_value,
deposit,
gas.limit,
gas.price,
nonce,
chain_id,
exec,
);

from_sk.zeroize();

Ok(tx.into())
}
/// Executes a generic contract call, paying gas with phoenix notes
pub async fn phoenix_execute(
&self,
sender: &Address,
reciever: &Address,
deposit: Dusk,
data: Option<impl Into<TransactionData>>,
gas: Gas,
data: Option<impl Into<TransactionData>>,
) -> Result<Transaction, Error> {
// make sure we own the sender address
if !sender.is_owned() {
Expand All @@ -422,6 +488,8 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
let mut rng = StdRng::from_entropy();
let sender_index = sender.index()?;
let mut sender_sk = derive_phoenix_sk(seed, sender_index);
// in a contract execution, the sender and receiver are the same
let receiver_pk = sender.pk();

let inputs = state
.inputs(sender_index, deposit + gas.limit * gas.price)?
Expand All @@ -436,7 +504,7 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
&mut rng,
&sender_sk,
sender.pk(),
reciever.pk(),
receiver_pk,
inputs,
root,
0,
Expand All @@ -455,8 +523,8 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
tx
}

/// Transfers funds between addresses
pub async fn transfer(
/// Transfers funds between phoenix-addresses
pub async fn phoenix_transfer(
&self,
sender: &Address,
rcvr: &Address,
Expand Down Expand Up @@ -510,7 +578,7 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
gas.limit,
gas.price,
chain_id,
None::<ContractCall>,
None::<TransactionData>,
)?;

let tx = state.prove_and_propagate(tx);
Expand All @@ -520,8 +588,8 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
tx
}

/// Stakes Dusk
pub async fn stake(
/// Stakes Dusk using phoenix notes
pub async fn phoenix_stake(
&self,
addr: &Address,
amt: Dusk,
Expand Down Expand Up @@ -579,19 +647,18 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
/// Obtains stake information for a given address
pub async fn stake_info(
&self,
addr: &Address,
addr_idx: u8,
) -> Result<Option<StakeData>, Error> {
let seed = self.store.get_seed();

self.state()?
.fetch_stake(&AccountPublicKey::from(&derive_bls_sk(
seed,
addr.index()?,
seed, addr_idx,
)))
}

/// Unstakes Dusk
pub async fn unstake(
/// Unstakes Dusk into phoenix notes
pub async fn phoenix_unstake(
&self,
addr: &Address,
gas: Gas,
Expand Down Expand Up @@ -642,25 +709,26 @@ impl<F: SecureWalletFile + Debug> Wallet<F> {
}

/// Withdraw accumulated staking reward for a given address
pub async fn withdraw_reward(
pub async fn phoenix_stake_withdraw(
&self,
addr: &Address,
sender_addr: &Address,
staker_index: u8,
gas: Gas,
) -> Result<Transaction, Error> {
let state = self.state()?;
// make sure we own the staking address
if !addr.is_owned() {
if !sender_addr.is_owned() {
return Err(Error::Unauthorized);
}

let mut rng = StdRng::from_entropy();
let index = addr.index()?;
let sender_index = sender_addr.index()?;
let seed = self.store.get_seed();

let mut sender_sk = derive_phoenix_sk(seed, index);
let mut stake_sk = derive_bls_sk(seed, index);
let mut sender_sk = derive_phoenix_sk(seed, sender_index);
let mut stake_sk = derive_bls_sk(seed, staker_index);

let inputs = state.inputs(index, gas.limit * gas.price)?;
let inputs = state.inputs(sender_index, gas.limit * gas.price)?;

let root = state.fetch_root()?;
let chain_id = state.fetch_chain_id()?;
Expand Down
75 changes: 75 additions & 0 deletions rusk-wallet/tests/generate_test_wallet.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) DUSK NETWORK. All rights reserved.

use wallet_core::keys::RNG_SEED;

// Create a wallet for testing where the seed is an array of `0u8`.
//
// Since there is no functionality to override the seed or address-count in
// a wallet (and there also shouldn't be one), we modified the `save` method
// (called by `save_to`) of the wallet so that the seed is overridden with
// `[0u8; RNG_SEED]` and the address count with `100`.
//
// For the asserts to work the `store` field must be pub, as well as the
// `LocalStore` struct
//
// Create the wallet-file by doing the above adjustments, uncomment the
// `#[test]` line below and run
// `cargo test --release --test generate_test_wallet`.
// The generated 'test_wallet.dat' file can then be moved to where it is needed.
//
// #[test]
fn create_test_wallet() -> Result<(), rusk_wallet::Error> {
use rusk_wallet::{SecureWalletFile, Wallet, WalletPath};

#[derive(Debug, Clone)]
struct WalletFile {
path: WalletPath,
pwd: Vec<u8>,
}

impl SecureWalletFile for WalletFile {
fn path(&self) -> &WalletPath {
&self.path
}

fn pwd(&self) -> &[u8] {
&self.pwd
}
}

// create the wallet file path
let wallet_path = WalletPath::from(
std::env::current_dir()?.as_path().join("test_wallet.dat"),
);
let wallet_file = WalletFile {
path: wallet_path,
pwd: blake3::hash(b"mypassword").as_bytes().to_vec(),
};

// create a test wallet with an arbitrary passphrase (the seed will be
// overridden)
const PHRASE: &str = "park remain person kitchen mule spell knee armed position rail grid ankle";

let mut wallet = Wallet::new(PHRASE)?;

// since there is no functionality to override the seed or address-count in
// a wallet (and there also shouldn't be one), we modified the `save` method
// (called by `save_to`) of the wallet so that the seed is overridden with
// `[0u8; RNG_SEED]` and the address count with `100`.
wallet.save_to(wallet_file.clone())?;

// load wallet from file and check seed and address count
// for these assert to work the `store` field must be pub, as well as the
// `LocalStore` struct
const SEED: [u8; RNG_SEED] = [0u8; RNG_SEED];
const ADDR_COUNT: u8 = 100;

let loaded_wallet = Wallet::from_file(wallet_file)?;
assert_eq!(*loaded_wallet.store.get_seed(), SEED);
assert_eq!(loaded_wallet.addresses().len(), ADDR_COUNT as usize);
Ok(())
}

0 comments on commit 7df7df7

Please sign in to comment.