setting.rs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::{
  19. io::Cursor,
  20. sync::{Arc, Mutex as SyncMutex},
  21. };
  22. use darkfi_serial::{Decodable, Encodable};
  23. use crate::{
  24. db::AppDbPtr,
  25. error::{Error, Result},
  26. prop::{
  27. Property, PropertyAtomicGuard, PropertyPermission, PropertyPtr, PropertySubType,
  28. PropertyType, PropertyValue, Role,
  29. },
  30. scene::{CallArgType, Pimpl, SceneNode, SceneNodeType, SceneNodeWeak},
  31. theme, ExecutorPtr,
  32. };
  33. /// Settings when modified are persisted otherwise they use their default
  34. /// as expected by properties.
  35. ///
  36. /// Settings can be used directly by other nodes or as a dependency which
  37. /// can be used in expressions.
  38. ///
  39. /// Settings are set by the user with `Role::User`.
  40. ///
  41. /// For example `net.enable_tor` might be implicitly used by `/plugin/darkirc`
  42. /// while some other setting might be used in the schema itself with the node
  43. /// not being aware its depending on an external property.
  44. ///
  45. /// In both cases modifying the setting should propagate the changes to that node.
  46. ///
  47. /// Although the `/setting` root has no knowledge of property paths underneath there
  48. /// is a convention of using `foo.bar.baz` to namespace the settings.
  49. /// Testnet endpoint from drk_config.toml
  50. const DARKFID_ENDPOINT_TCP: &str = "tcp+tls://node0.testnet.dark.fi:18345";
  51. /// TODO: replace with the real darkfid tor endpoint
  52. const DARKFID_ENDPOINT_TOR: &str = "tor://darkfid-tor-placeholder.onion:18345";
  53. pub fn create_setting(name: &str) -> SceneNode {
  54. let mut node = SceneNode::new(name, SceneNodeType::Setting);
  55. let mut prop = Property::new(
  56. "chat.is_enabled",
  57. PropertyType::Bool,
  58. PropertySubType::Flag,
  59. PropertyPermission::default(),
  60. );
  61. prop.set_defaults_bool(vec![true]).unwrap();
  62. node.add_property(prop).unwrap();
  63. let mut prop = Property::new(
  64. "net.transport",
  65. PropertyType::Enum,
  66. PropertySubType::Null,
  67. PropertyPermission::default(),
  68. );
  69. prop.set_enum_items(vec!["tcp", "tor"]).unwrap();
  70. prop.set_default_enum(0, "tcp").unwrap();
  71. node.add_property(prop).unwrap();
  72. let mut prop = Property::new(
  73. "wallet.tcp.rpc",
  74. PropertyType::Str,
  75. PropertySubType::Null,
  76. PropertyPermission::default(),
  77. );
  78. prop.set_defaults_str(vec![DARKFID_ENDPOINT_TCP.to_string()]).unwrap();
  79. node.add_property(prop).unwrap();
  80. let mut prop = Property::new(
  81. "wallet.tor.rpc",
  82. PropertyType::Str,
  83. PropertySubType::Null,
  84. PropertyPermission::default(),
  85. );
  86. prop.set_defaults_str(vec![DARKFID_ENDPOINT_TOR.to_string()]).unwrap();
  87. node.add_property(prop).unwrap();
  88. let mut prop = Property::new(
  89. "theme",
  90. PropertyType::Enum,
  91. PropertySubType::Null,
  92. PropertyPermission::default(),
  93. );
  94. prop.set_enum_items(theme::registry().iter().map(|t| t.name()).collect::<Vec<_>>()).unwrap();
  95. // Default scifi: the shipped look. A proper Enum variant default
  96. // (set_default_enum), unlike the Str-on-Enum latent mismatch above.
  97. prop.set_default_enum(0, theme::DEFAULT_THEME).unwrap();
  98. node.add_property(prop).unwrap();
  99. let mut prop = Property::new(
  100. "win.scale",
  101. PropertyType::Float32,
  102. PropertySubType::Null,
  103. PropertyPermission::default(),
  104. );
  105. prop.set_defaults_f32(vec![1.]).unwrap();
  106. prop.set_range_f32(0., f32::MAX);
  107. node.add_property(prop).unwrap();
  108. node.add_method(
  109. "search",
  110. vec![("filter", "Filter string to search keys", CallArgType::Str)],
  111. None,
  112. )
  113. .unwrap();
  114. node
  115. }
  116. pub type SettingPtr = Arc<Setting>;
  117. pub struct Setting {
  118. tasks: SyncMutex<Vec<smol::Task<()>>>,
  119. }
  120. impl Setting {
  121. pub async fn new(node: SceneNodeWeak, app_db: AppDbPtr, ex: ExecutorPtr) -> Pimpl {
  122. let node_ref = node.upgrade().unwrap();
  123. // Load any persisted properties from the db in a fail-safe way
  124. let rows = match app_db.settings_all().await {
  125. Ok(rows) => rows,
  126. Err(e) => {
  127. error!(target: "app::setting", "Failed to read settings from db, using defaults: {e}");
  128. vec![]
  129. }
  130. };
  131. for (name, idx, data) in rows {
  132. let Some(prop) = node_ref.get_property(&name) else {
  133. warn!(target: "app::setting", "Skipping unknown persisted setting: {name}");
  134. continue
  135. };
  136. if let Err(e) = Self::load_prop(&prop, idx, &data) {
  137. warn!(target: "app::setting", "Skipping persisted setting {name}[{idx}]: {e}");
  138. }
  139. }
  140. // Spawn tasks persisting our properties to the db when they change.
  141. let mut tasks = vec![];
  142. for prop in &node_ref.props {
  143. let app_db2 = app_db.clone();
  144. let prop2 = prop.clone();
  145. let on_modify_sub = prop.subscribe_modify();
  146. let task = ex.spawn(async move {
  147. while let Ok((_role, _action, _guard)) = on_modify_sub.receive().await {
  148. Self::save_prop(&prop2, &app_db2).await.unwrap();
  149. }
  150. });
  151. tasks.push(task);
  152. }
  153. Pimpl::Setting(Arc::new(Self { tasks: SyncMutex::new(tasks) }))
  154. }
  155. /// Persist the state of a property: one row per set index keyed by
  156. /// (prop name, idx). Unset indexes have their row deleted.
  157. async fn save_prop(prop: &PropertyPtr, app_db: &AppDbPtr) -> Result<()> {
  158. assert!(prop.is_bounded());
  159. for i in 0..prop.get_len() {
  160. if prop.is_unset(i)? {
  161. app_db.setting_remove_idx(&prop.name, i as u32).await?;
  162. continue
  163. }
  164. let val = prop.get_value(i)?;
  165. let mut data = vec![];
  166. Self::encode_value(prop, &val, &mut data)?;
  167. app_db.setting_put(&prop.name, i as u32, "prop", &data).await?;
  168. }
  169. Ok(())
  170. }
  171. /// Serialize a single value. The property type determines the binary
  172. /// format of the value itself. If the property allows null values then
  173. /// it is written as an option (`Option<X>`): a single tag byte followed
  174. /// by the value only when it is not null.
  175. fn encode_value(prop: &Property, val: &PropertyValue, data: &mut Vec<u8>) -> Result<()> {
  176. if prop.is_null_allowed {
  177. match val {
  178. PropertyValue::Null => {
  179. false.encode(data)?;
  180. }
  181. val => {
  182. true.encode(data)?;
  183. val.encode(data)?;
  184. }
  185. }
  186. } else {
  187. val.encode(data)?;
  188. }
  189. Ok(())
  190. }
  191. /// Restore the value of a property at `idx` previously written by
  192. /// `Self::save_prop()`.
  193. fn load_prop(prop: &PropertyPtr, idx: u32, data: &[u8]) -> Result<()> {
  194. assert!(prop.is_bounded());
  195. let mut cur = Cursor::new(data);
  196. let atom = &mut PropertyAtomicGuard::none();
  197. let val = Self::decode_value(prop, &mut cur)?;
  198. Self::apply_value(prop, atom, idx as usize, val)?;
  199. Ok(())
  200. }
  201. /// Decode a value serialized by `Self::encode_value()`.
  202. fn decode_value(prop: &Property, cur: &mut Cursor<&[u8]>) -> Result<PropertyValue> {
  203. macro_rules! decode_ty {
  204. ($typ:ty, $variant:ident) => {{
  205. if prop.is_null_allowed {
  206. match Option::<$typ>::decode(cur)? {
  207. Some(v) => PropertyValue::$variant(v),
  208. None => PropertyValue::Null,
  209. }
  210. } else {
  211. PropertyValue::$variant(<$typ>::decode(cur)?)
  212. }
  213. }};
  214. }
  215. let val = match prop.typ {
  216. PropertyType::Bool => decode_ty!(bool, Bool),
  217. PropertyType::Uint32 => decode_ty!(u32, Uint32),
  218. PropertyType::Float32 => decode_ty!(f32, Float32),
  219. PropertyType::Str => decode_ty!(String, Str),
  220. PropertyType::Enum => decode_ty!(String, Enum),
  221. PropertyType::SceneNodeId => decode_ty!(u32, SceneNodeId),
  222. PropertyType::Null | PropertyType::SExpr | PropertyType::VectorShape => {
  223. return Err(Error::PropertyWrongType)
  224. }
  225. };
  226. Ok(val)
  227. }
  228. /// Set the value at index `i` as `Role::User`.
  229. fn apply_value(
  230. prop: &PropertyPtr,
  231. atom: &mut PropertyAtomicGuard,
  232. i: usize,
  233. val: PropertyValue,
  234. ) -> Result<()> {
  235. match val {
  236. PropertyValue::Bool(v) => prop.set_bool(atom, Role::User, i, v),
  237. PropertyValue::Uint32(v) => prop.set_u32(atom, Role::User, i, v),
  238. PropertyValue::Float32(v) => prop.set_f32(atom, Role::User, i, v),
  239. PropertyValue::Str(v) => prop.set_str(atom, Role::User, i, v),
  240. PropertyValue::Enum(v) => prop.set_enum(atom, Role::User, i, v),
  241. PropertyValue::SceneNodeId(v) => prop.set_node_id(atom, Role::User, i, v),
  242. PropertyValue::Null => prop.set_null(atom, Role::User, i),
  243. PropertyValue::Unset | PropertyValue::SExpr(_) | PropertyValue::VectorShape(_) => {
  244. Err(Error::PropertyWrongType)
  245. }
  246. }
  247. }
  248. }
  249. impl Drop for Setting {
  250. fn drop(&mut self) {
  251. self.tasks.lock().unwrap().clear();
  252. }
  253. }