Skip to content

Commit

Permalink
feat(application): implement withdraws
Browse files Browse the repository at this point in the history
  • Loading branch information
matthias-wright committed Dec 16, 2024
1 parent fe26219 commit 4c9873c
Show file tree
Hide file tree
Showing 9 changed files with 167 additions and 7 deletions.
2 changes: 2 additions & 0 deletions core/application/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ impl ApplicationEnv {

metadata_table.insert(Metadata::BlockNumber, Value::BlockNumber(0));

metadata_table.insert(Metadata::WithdrawId, Value::WithdrawId(0));

metadata_table.insert(
Metadata::ProtocolFundAddress,
Value::AccountPublicKey(genesis.protocol_fund_address),
Expand Down
55 changes: 50 additions & 5 deletions core/application/src/state/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ pub struct StateExecutor<B: Backend> {
),
>,
pub committee_selection_beacon_non_revealing_node: B::Ref<NodeIndex, ()>,
pub flk_withdraws: B::Ref<u64, (EthAddress, HpUfixed<18>)>,
pub usdc_withdraws: B::Ref<u64, (EthAddress, HpUfixed<6>)>,
pub backend: B,
}

Expand Down Expand Up @@ -162,6 +164,8 @@ impl<B: Backend> StateExecutor<B> {
committee_selection_beacon: backend.get_table_reference("committee_selection_beacon"),
committee_selection_beacon_non_revealing_node: backend
.get_table_reference("committee_selection_beacon_non_revealing_node"),
flk_withdraws: backend.get_table_reference("flk_withdraws"),
usdc_withdraws: backend.get_table_reference("usdc_withdraws"),
backend,
}
}
Expand Down Expand Up @@ -466,12 +470,53 @@ impl<B: Backend> StateExecutor<B> {

fn withdraw(
&self,
_sender: TransactionSender,
_reciever: EthAddress,
_amount: HpUfixed<18>,
_token: Tokens,
sender: TransactionSender,
reciever: EthAddress,
amount: HpUfixed<18>,
token: Tokens,
) -> TransactionResponse {
TransactionResponse::Revert(ExecutionError::Unimplemented)
// This transaction is only callable by AccountOwners and not nodes
// So revert if the sender is a node public key
let sender = match self.only_account_owner(sender) {
Ok(account) => account,
Err(e) => return e,
};
let Some(mut account) = self.account_info.get(&sender) else {
return TransactionResponse::Revert(ExecutionError::AccountDoesNotExist);
};

let withdraw_id = match self.metadata.get(&Metadata::WithdrawId) {
Some(Value::WithdrawId(epoch)) => epoch,
_ => 0,
};

match token {
Tokens::FLK => {
if amount > account.flk_balance {
return TransactionResponse::Revert(ExecutionError::InsufficientBalance);
}

account.flk_balance -= amount.clone();
self.flk_withdraws.set(withdraw_id, (reciever, amount));
self.metadata
.set(Metadata::WithdrawId, Value::WithdrawId(withdraw_id + 1));
},
Tokens::USDC => {
// TODO(matthias): make sure that this conversion is safe
let amount = amount.convert_precision::<6>();
if amount > account.stables_balance {
return TransactionResponse::Revert(ExecutionError::InsufficientBalance);
}

account.stables_balance -= amount.clone();
self.usdc_withdraws.set(withdraw_id, (reciever, amount));
self.metadata
.set(Metadata::WithdrawId, Value::WithdrawId(withdraw_id + 1));
},
}

self.account_info.set(sender, account);
TransactionResponse::Success(ExecutionData::None)
}

fn deposit(
Expand Down
6 changes: 6 additions & 0 deletions core/application/src/state/executor/epoch_change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,12 @@ impl<B: Backend> StateExecutor<B> {
// Clear executed digests.
self.executed_digests.clear();

// Clear withdraws
self.flk_withdraws.clear();
self.usdc_withdraws.clear();
self.metadata
.set(Metadata::WithdrawId, Value::WithdrawId(0));

self.committee_info.set(epoch, current_committee);
// Get new committee
let new_committee = self.choose_new_committee(beacons);
Expand Down
21 changes: 20 additions & 1 deletion core/application/src/state/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ pub struct QueryRunner {
),
>,
committee_selection_beacon_non_revealing_node: ResolvedTableReference<NodeIndex, ()>,
flk_withdraws: ResolvedTableReference<u64, (EthAddress, HpUfixed<18>)>,
usdc_withdraws: ResolvedTableReference<u64, (EthAddress, HpUfixed<6>)>,
}

impl QueryRunner {
Expand Down Expand Up @@ -122,7 +124,8 @@ impl SyncQueryRunnerInterface for QueryRunner {
)>("committee_selection_beacon"),
committee_selection_beacon_non_revealing_node: atomo
.resolve::<NodeIndex, ()>("committee_selection_beacon_non_revealing_node"),

flk_withdraws: atomo.resolve::<u64, (EthAddress, HpUfixed<18>)>("flk_withdraws"),
usdc_withdraws: atomo.resolve::<u64, (EthAddress, HpUfixed<6>)>("usdc_withdraws"),
inner: atomo,
}
}
Expand Down Expand Up @@ -372,4 +375,20 @@ impl SyncQueryRunnerInterface for QueryRunner {
// This is consistent with the logic in `Env::apply_genesis_block`.
self.get_metadata(&Metadata::Epoch).is_some()
}

fn get_flk_withdraws(&self) -> Vec<(u64, EthAddress, HpUfixed<18>)> {
self.inner
.run(|ctx| self.flk_withdraws.get(ctx).as_map())
.iter()
.map(|(id, (address, amount))| (*id, *address, amount.clone()))
.collect()
}

fn get_usdc_withdraws(&self) -> Vec<(u64, EthAddress, HpUfixed<6>)> {
self.inner
.run(|ctx| self.usdc_withdraws.get(ctx).as_map())
.iter()
.map(|(id, (address, amount))| (*id, *address, amount.clone()))
.collect()
}
}
6 changes: 5 additions & 1 deletion core/application/src/state/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,8 @@ impl ApplicationState<AtomoStorage, DefaultSerdeBackend, ApplicationStateTree> {
Option<CommitteeSelectionBeaconReveal>,
)>("committee_selection_beacon")
.with_table::<NodeIndex, ()>("committee_selection_beacon_non_revealing_node")
.with_table::<u64, (EthAddress, HpUfixed<18>)>("flk_withdraws")
.with_table::<u64, (EthAddress, HpUfixed<6>)>("usdc_withdraws")
.enable_iter("current_epoch_served")
.enable_iter("rep_measurements")
.enable_iter("submitted_rep_measurements")
Expand All @@ -200,7 +202,9 @@ impl ApplicationState<AtomoStorage, DefaultSerdeBackend, ApplicationStateTree> {
.enable_iter("uri_to_node")
.enable_iter("node_to_uri")
.enable_iter("committee_selection_beacon")
.enable_iter("committee_selection_beacon_non_revealing_node");
.enable_iter("committee_selection_beacon_non_revealing_node")
.enable_iter("flk_withdraws")
.enable_iter("usdc_withdraws");

#[cfg(debug_assertions)]
{
Expand Down
74 changes: 74 additions & 0 deletions core/application/src/tests/balances.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ use hp_fixed::unsigned::HpUfixed;
use lightning_interfaces::types::{
ExecutionData,
ExecutionError,
GenesisAccount,
ProofOfConsensus,
Tokens,
UpdateMethod,
};
use lightning_interfaces::SyncQueryRunnerInterface;
use tempfile::tempdir;

use super::utils::*;
Expand Down Expand Up @@ -221,3 +223,75 @@ async fn test_deposit_usdc_works_properly() {
intial_balance + deposit_amount
);
}

#[tokio::test]
async fn test_withdraw_usdc_works_properly() {
let temp_dir = tempdir().unwrap();

let mut genesis = test_genesis();

let owner_secret_key = AccountOwnerSecretKey::generate();
let owner: EthAddress = owner_secret_key.to_pk().into();

let receiver_secret_key = AccountOwnerSecretKey::generate();
let receiver: EthAddress = receiver_secret_key.to_pk().into();

let account = GenesisAccount {
public_key: owner,
flk_balance: 0_u64.into(),
stables_balance: 1000,
bandwidth_balance: 0,
};
genesis.account = vec![account];

let (update_socket, query_runner) = init_app_with_genesis(&temp_dir, &genesis);

let withdraw_amount = 500_u64;
let withdraw = UpdateMethod::Withdraw {
amount: withdraw_amount.into(),
token: Tokens::USDC,
receiving_address: receiver,
};
let update = prepare_update_request_account(withdraw, &owner_secret_key, 1);
expect_tx_success(update, &update_socket, ExecutionData::None).await;

let withdraws = query_runner.get_usdc_withdraws();
assert_eq!(withdraws[0].1, receiver);
assert_eq!(withdraws[0].2, withdraw_amount.into());
}

#[tokio::test]
async fn test_withdraw_flk_works_properly() {
let temp_dir = tempdir().unwrap();

let mut genesis = test_genesis();

let owner_secret_key = AccountOwnerSecretKey::generate();
let owner: EthAddress = owner_secret_key.to_pk().into();

let receiver_secret_key = AccountOwnerSecretKey::generate();
let receiver: EthAddress = receiver_secret_key.to_pk().into();

let account = GenesisAccount {
public_key: owner,
flk_balance: 1000_u64.into(),
stables_balance: 0,
bandwidth_balance: 0,
};
genesis.account = vec![account];

let (update_socket, query_runner) = init_app_with_genesis(&temp_dir, &genesis);

let withdraw_amount = 500_u64;
let withdraw = UpdateMethod::Withdraw {
amount: withdraw_amount.into(),
token: Tokens::FLK,
receiving_address: receiver,
};
let update = prepare_update_request_account(withdraw, &owner_secret_key, 1);
expect_tx_success(update, &update_socket, ExecutionData::None).await;

let withdraws = query_runner.get_flk_withdraws();
assert_eq!(withdraws[0].1, receiver);
assert_eq!(withdraws[0].2, withdraw_amount.into());
}
7 changes: 7 additions & 0 deletions core/interfaces/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use atomo::{Atomo, InMemoryStorage, KeyIterator, QueryPerm, StorageBackend};
use fdi::BuildGraph;
use fleek_crypto::{ClientPublicKey, EthAddress, NodePublicKey};
use fxhash::FxHashMap;
use hp_fixed::unsigned::HpUfixed;
use lightning_types::{
AccountInfo,
Blake3Hash,
Expand Down Expand Up @@ -240,6 +241,12 @@ pub trait SyncQueryRunnerInterface: Clone + Send + Sync + 'static {

// Returns whether the genesis block has been applied.
fn has_genesis(&self) -> bool;

/// Returns a list of FLK withdraws
fn get_flk_withdraws(&self) -> Vec<(u64, EthAddress, HpUfixed<18>)>;

/// Returns a list of USDC withdraws
fn get_usdc_withdraws(&self) -> Vec<(u64, EthAddress, HpUfixed<6>)>;
}

#[derive(Clone, Debug)]
Expand Down
1 change: 1 addition & 0 deletions core/types/src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ pub enum ExecutionError {
NotNodeOwner,
NotCommitteeMember,
NodeDoesNotExist,
AccountDoesNotExist,
CantSendToYourself,
AlreadySignaled,
SubmittedTooManyTransactions,
Expand Down
2 changes: 2 additions & 0 deletions core/types/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub enum Metadata {
CommitteeSelectionBeaconPhase,
CommitteeSelectionBeaconRound,
EpochEra,
WithdrawId,
}

/// The Value enum is a data type used to represent values in a key-value pair for a metadata table
Expand All @@ -127,6 +128,7 @@ pub enum Value {
CommitteeSelectionBeaconPhase(CommitteeSelectionBeaconPhase),
CommitteeSelectionBeaconRound(CommitteeSelectionBeaconRound),
EpochEra(u64),
WithdrawId(u64),
}

impl Value {
Expand Down

0 comments on commit 4c9873c

Please sign in to comment.