Skip to content

Commit

Permalink
Merge pull request #1244 from getlipa/feature/change-fiat-currency-co…
Browse files Browse the repository at this point in the history
…nfig-to-default

Change fiat currency config to a default
  • Loading branch information
danielgranhao authored Oct 30, 2024
2 parents a0f5eca + 412030e commit 8193f43
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 30 deletions.
22 changes: 9 additions & 13 deletions examples/node/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,17 +77,9 @@ pub(crate) fn poll_for_user_input(node: &LightningNode, log_file_path: &str) {
list_currency_codes(node);
}
"changecurrency" => {
match words
.next()
.ok_or_else(|| "Error: fiat currency code is required".to_string())
{
Ok(c) => {
change_currency(node, c);
}
Err(e) => {
println!("{}", e.red());
}
};
if let Err(message) = change_currency(node, &mut words) {
println!("{}", format!("{message:#}").red());
}
}
"changetimezone" => {
if let Err(message) = change_timezone(node, &mut words) {
Expand Down Expand Up @@ -706,8 +698,12 @@ fn list_currency_codes(node: &LightningNode) {
println!("Supported currencies: {codes:?}");
}

fn change_currency(node: &LightningNode, fiat_currency: &str) {
node.change_fiat_currency(String::from(fiat_currency));
fn change_currency(node: &LightningNode, words: &mut dyn Iterator<Item = &str>) -> Result<()> {
let fiat_currency = words
.next()
.ok_or(anyhow!("Fiat currency code is required"))?;
node.change_fiat_currency(String::from(fiat_currency))?;
Ok(())
}

fn change_timezone(node: &LightningNode, words: &mut dyn Iterator<Item = &str>) -> Result<()> {
Expand Down
2 changes: 1 addition & 1 deletion examples/node/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ fn main() {

let config = Config {
seed,
fiat_currency: "EUR".to_string(),
default_fiat_currency: "EUR".to_string(),
local_persistence_path: base_dir.clone(),
timezone_config: TzConfig {
timezone_id: String::from("Africa/Tunis"),
Expand Down
2 changes: 1 addition & 1 deletion examples/notification_handler/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ fn get_config() -> Config {

Config {
seed,
fiat_currency: "EUR".to_string(),
default_fiat_currency: "EUR".to_string(),
local_persistence_path: base_dir.clone(),
timezone_config: TzConfig {
timezone_id: String::from("Africa/Tunis"),
Expand Down
6 changes: 5 additions & 1 deletion src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ pub struct Config {
/// ISO 4217 currency code. The backend does not support all of them, but supports at least USD
/// and EUR, so it is safe to default to one of them. Providing an invalid code will result in
/// missing fiat values for payments.
pub fiat_currency: String,
///
/// The provided value is used as a default. After the first time the node is started,
/// this config starts being ignored. Changing the fiat currency can be done using
/// [`crate::LightningNode::change_fiat_currency`].
pub default_fiat_currency: String,
/// A path on the local filesystem where this library will directly persist data. Only the
/// current instance of the app should have access to the provided directory. On app
/// uninstall/deletion, the directory should be purged.
Expand Down
54 changes: 53 additions & 1 deletion src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ impl DataStore {
Ok(())
}

pub fn retrieve_hidden_unresolved_failed_swaps(&mut self) -> Result<Vec<String>> {
pub fn retrieve_hidden_unresolved_failed_swaps(&self) -> Result<Vec<String>> {
Ok(self
.conn
.prepare("SELECT swap_address FROM hidden_failed_swaps")
Expand All @@ -475,6 +475,28 @@ impl DataStore {
.filter_map(|r| r.ok())
.collect::<Vec<_>>())
}

pub fn store_selected_fiat_currency(&mut self, fiat_currency: &str) -> Result<()> {
self.backup_status = BackupStatus::WaitingForBackup;
self.conn
.execute(
"INSERT INTO fiat_currency (fiat_currency) VALUES (?1)",
params![fiat_currency],
)
.map_to_permanent_failure("Failed to store fiat currency in db")?;
Ok(())
}

pub fn retrieve_last_set_fiat_currency(&self) -> Result<Option<String>> {
self.conn
.query_row(
"SELECT fiat_currency FROM fiat_currency ORDER BY id DESC LIMIT 1",
(),
|r| r.get(0),
)
.optional()
.map_to_permanent_failure("Failed to query last hidden channel close amount")
}
}

fn lightning_address_from_row(row: &Row) -> rusqlite::Result<(String, EnableStatus)> {
Expand Down Expand Up @@ -1417,6 +1439,36 @@ mod tests {
);
}

#[test]
fn test_storing_fiat_currency() {
let db_name = String::from("fiat_currency.db3");
reset_db(&db_name);
let mut data_store = DataStore::new(&format!("{TEST_DB_PATH}/{db_name}")).unwrap();

assert!(data_store
.retrieve_last_set_fiat_currency()
.unwrap()
.is_none());

data_store.store_selected_fiat_currency("EUR").unwrap();
assert_eq!(
data_store
.retrieve_last_set_fiat_currency()
.unwrap()
.unwrap(),
"EUR"
);

data_store.store_selected_fiat_currency("CHF").unwrap();
assert_eq!(
data_store
.retrieve_last_set_fiat_currency()
.unwrap()
.unwrap(),
"CHF"
);
}

fn reset_db(db_name: &str) {
let _ = fs::create_dir(TEST_DB_PATH);
let _ = fs::remove_file(format!("{TEST_DB_PATH}/{db_name}"));
Expand Down
24 changes: 19 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,21 @@ impl LightningNode {
&config.remote_services_config.backend_url,
)?);

let db_path = format!("{}/{DB_FILENAME}", config.local_persistence_path);
let mut data_store = DataStore::new(&db_path)?;

let fiat_currency = match data_store.retrieve_last_set_fiat_currency()? {
None => {
data_store.store_selected_fiat_currency(&config.default_fiat_currency)?;
config.default_fiat_currency.clone()
}
Some(c) => c,
};

let data_store = Arc::new(Mutex::new(data_store));

let user_preferences = Arc::new(Mutex::new(UserPreferences {
fiat_currency: config.fiat_currency.clone(),
fiat_currency,
timezone_config: config.timezone_config.clone(),
}));

Expand All @@ -395,9 +408,6 @@ impl LightningNode {
Arc::clone(&async_auth),
);

let db_path = format!("{}/{DB_FILENAME}", config.local_persistence_path);
let data_store = Arc::new(Mutex::new(DataStore::new(&db_path)?));

let analytics_config = data_store.lock_unwrap().retrieve_analytics_config()?;
let analytics_interceptor = Arc::new(AnalyticsInterceptor::new(
analytics_client,
Expand Down Expand Up @@ -1567,8 +1577,12 @@ impl LightningNode {
/// The method [`LightningNode::list_currency_codes`] can used to list supported codes.
///
/// Requires network: **no**
pub fn change_fiat_currency(&self, fiat_currency: String) {
pub fn change_fiat_currency(&self, fiat_currency: String) -> Result<()> {
self.data_store
.lock_unwrap()
.store_selected_fiat_currency(&fiat_currency)?;
self.user_preferences.lock_unwrap().fiat_currency = fiat_currency;
Ok(())
}

/// Change the timezone config.
Expand Down
3 changes: 2 additions & 1 deletion src/lipalightninglib.udl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ interface LightningNode {

ExchangeRate? get_exchange_rate();

[Throws=LnError]
void change_fiat_currency(string fiat_currency);

void change_timezone_config(TzConfig timezone_config);
Expand Down Expand Up @@ -194,7 +195,7 @@ interface LightningNode {

dictionary Config {
bytes seed;
string fiat_currency;
string default_fiat_currency;
string local_persistence_path;
TzConfig timezone_config;
Level? file_logging_level;
Expand Down
9 changes: 9 additions & 0 deletions src/migrations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,14 @@ const MIGRATION_17_HIDDEN_FAILED_SWAPS: &str = "
);
";

const MIGRATION_18_FIAT_CURRENCY: &str = "
CREATE TABLE fiat_currency (
id INTEGER NOT NULL PRIMARY KEY,
fiat_currency TEXT NOT NULL,
inserter_at INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP
);
";

pub(crate) fn migrate(conn: &mut Connection) -> Result<()> {
migrations()
.to_latest(conn)
Expand All @@ -179,6 +187,7 @@ fn migrations() -> Migrations<'static> {
M::up(MIGRATION_15_LIGHTNING_ADDRESSES_ENABLE_STATUS),
M::up(MIGRATION_16_HIDDEN_CHANNEL_CLOSE_AMOUNT),
M::up(MIGRATION_17_HIDDEN_FAILED_SWAPS),
M::up(MIGRATION_18_FIAT_CURRENCY),
])
}

Expand Down
23 changes: 18 additions & 5 deletions src/notification_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,18 @@ fn build_analytics_interceptor(
config: &Config,
rt: &AsyncRuntime,
) -> NotificationHandlingResult<AnalyticsInterceptor> {
let db_path = format!("{}/{DB_FILENAME}", config.local_persistence_path);
let data_store = DataStore::new(&db_path)
.map_runtime_error_using(NotificationHandlingErrorCode::from_runtime_error)?;

let fiat_currency = data_store
.retrieve_last_set_fiat_currency()
.map_runtime_error_using(NotificationHandlingErrorCode::from_runtime_error)?
.ok_or(permanent_failure(
"No fiat currency set. Node must be started before handling notifications",
))?;
let user_preferences = Arc::new(Mutex::new(UserPreferences {
fiat_currency: config.fiat_currency.clone(),
fiat_currency,
timezone_config: config.timezone_config.clone(),
}));

Expand All @@ -175,9 +185,6 @@ fn build_analytics_interceptor(
Arc::clone(&async_auth),
);

let db_path = format!("{}/{DB_FILENAME}", config.local_persistence_path);
let data_store = DataStore::new(&db_path)
.map_runtime_error_using(NotificationHandlingErrorCode::from_runtime_error)?;
let analytics_config = data_store
.retrieve_analytics_config()
.map_runtime_error_using(NotificationHandlingErrorCode::from_runtime_error)?;
Expand Down Expand Up @@ -391,9 +398,15 @@ fn handle_lnurl_pay_request_notification(
// Invoice is not persisted in invoices table because we are not interested in unpaid invoices
// resulting from incoming LNURL payments

let fiat_currency = data_store
.retrieve_last_set_fiat_currency()
.map_runtime_error_using(NotificationHandlingErrorCode::from_runtime_error)?
.ok_or(permanent_failure(
"No fiat currency set. Node must be started before handling notifications",
))?;
// Store payment info (exchange rates, user preferences, etc...)
let user_preferences = UserPreferences {
fiat_currency: config.fiat_currency.clone(),
fiat_currency,
timezone_config: config.timezone_config.clone(),
};
let exchange_rate_provider = ExchangeRateProviderImpl::new(
Expand Down
2 changes: 1 addition & 1 deletion tests/register_node_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ fn test_register_node() {

let config = Config {
seed: secret.seed,
fiat_currency: "EUR".to_string(),
default_fiat_currency: "EUR".to_string(),
local_persistence_path: LOCAL_PERSISTENCE_PATH.to_string(),
timezone_config: TzConfig {
timezone_id: String::from("int_test_timezone_id"),
Expand Down
2 changes: 1 addition & 1 deletion tests/setup/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub fn start_specific_node(

let config = Config {
seed,
fiat_currency: "EUR".to_string(),
default_fiat_currency: "EUR".to_string(),
local_persistence_path,
timezone_config: TzConfig {
timezone_id: String::from("int_test_timezone_id"),
Expand Down

0 comments on commit 8193f43

Please sign in to comment.