2022-11-14 02:56:25 +00:00
// Gupax - GUI Uniting P2Pool And XMRig
//
2023-02-26 16:45:58 +00:00
// Copyright (c) 2022-2023 hinto-janai
2022-11-14 02:56:25 +00:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// This handles reading/writing the disk files:
// - [state.toml] -> [App] state
// - [nodes.toml] -> [Manual Nodes] list
// The TOML format is used. This struct hierarchy
// directly translates into the TOML parser:
// State/
// ├─ Gupax/
// │ ├─ ...
// ├─ P2pool/
// │ ├─ ...
// ├─ Xmrig/
// │ ├─ ...
// ├─ Version/
// ├─ ...
2022-11-16 19:40:25 +00:00
use std ::{
fs ,
fmt ::Display ,
path ::PathBuf ,
result ::Result ,
sync ::{ Arc , Mutex } ,
fmt ::Write ,
} ;
2022-11-14 02:56:25 +00:00
use serde ::{ Serialize , Deserialize } ;
use figment ::Figment ;
use figment ::providers ::{ Format , Toml } ;
2022-11-23 04:21:46 +00:00
use crate ::{
2022-12-26 22:37:45 +00:00
human ::* ,
2022-11-23 04:21:46 +00:00
constants ::* ,
gupax ::Ratio ,
2022-12-06 03:33:35 +00:00
Tab ,
2022-12-28 21:04:26 +00:00
xmr ::* ,
2022-12-29 03:03:45 +00:00
macros ::* ,
2022-11-23 04:21:46 +00:00
} ;
2022-11-14 02:56:25 +00:00
use log ::* ;
2022-12-30 14:39:03 +00:00
#[ cfg(target_family = " unix " ) ]
use std ::os ::unix ::fs ::PermissionsExt ;
2022-11-14 02:56:25 +00:00
2022-11-23 04:21:46 +00:00
//---------------------------------------------------------------------------------------------------- Const
// State file
2022-11-24 04:03:56 +00:00
const ERROR : & str = " Disk error " ;
const PATH_ERROR : & str = " PATH for state directory could not be not found " ;
2023-05-30 14:30:22 +00:00
2022-11-23 04:21:46 +00:00
#[ cfg(target_os = " windows " ) ]
2022-12-18 02:42:30 +00:00
const DIRECTORY : & str = r # "Gupax\"# ;
2022-11-23 04:21:46 +00:00
#[ cfg(target_os = " macos " ) ]
2022-12-18 02:42:30 +00:00
const DIRECTORY : & str = " Gupax/ " ;
2022-11-23 04:21:46 +00:00
#[ cfg(target_os = " linux " ) ]
2022-11-24 04:03:56 +00:00
const DIRECTORY : & str = " gupax/ " ;
2022-11-23 04:21:46 +00:00
2022-12-26 22:37:45 +00:00
// File names
pub const STATE_TOML : & str = " state.toml " ;
pub const NODE_TOML : & str = " node.toml " ;
pub const POOL_TOML : & str = " pool.toml " ;
// P2Pool API
// Lives within the Gupax OS data directory.
// ~/.local/share/gupax/p2pool/
2022-12-29 17:12:12 +00:00
// ├─ payout_log // Raw log lines of payouts received
// ├─ payout // Single [u64] representing total payouts
// ├─ xmr // Single [u64] representing total XMR mined in atomic units
2022-12-26 22:37:45 +00:00
#[ cfg(target_os = " windows " ) ]
pub const GUPAX_P2POOL_API_DIRECTORY : & str = r "p2pool\" ;
#[ cfg(target_family = " unix " ) ]
pub const GUPAX_P2POOL_API_DIRECTORY : & str = " p2pool/ " ;
2022-12-29 17:12:12 +00:00
pub const GUPAX_P2POOL_API_LOG : & str = " log " ;
pub const GUPAX_P2POOL_API_PAYOUT : & str = " payout " ;
pub const GUPAX_P2POOL_API_XMR : & str = " xmr " ;
2022-12-28 21:04:26 +00:00
pub const GUPAX_P2POOL_API_FILE_ARRAY : [ & str ; 3 ] = [
2022-12-29 17:12:12 +00:00
GUPAX_P2POOL_API_LOG ,
2022-12-26 22:37:45 +00:00
GUPAX_P2POOL_API_PAYOUT ,
2022-12-29 17:12:12 +00:00
GUPAX_P2POOL_API_XMR ,
2022-12-26 22:37:45 +00:00
] ;
2022-11-23 04:21:46 +00:00
#[ cfg(target_os = " windows " ) ]
2022-12-18 02:42:30 +00:00
pub const DEFAULT_P2POOL_PATH : & str = r "P2Pool\p2pool.exe" ;
#[ cfg(target_os = " macos " ) ]
2022-11-24 04:03:56 +00:00
pub const DEFAULT_P2POOL_PATH : & str = " p2pool/p2pool " ;
2022-11-23 04:21:46 +00:00
#[ cfg(target_os = " windows " ) ]
2022-12-18 02:42:30 +00:00
pub const DEFAULT_XMRIG_PATH : & str = r "XMRig\xmrig.exe" ;
#[ cfg(target_os = " macos " ) ]
pub const DEFAULT_XMRIG_PATH : & str = " xmrig/xmrig " ;
// Default to [/usr/bin/] for Linux distro builds.
#[ cfg(target_os = " linux " ) ]
#[ cfg(not(feature = " distro " )) ]
pub const DEFAULT_P2POOL_PATH : & str = " p2pool/p2pool " ;
#[ cfg(target_os = " linux " ) ]
#[ cfg(not(feature = " distro " )) ]
2022-11-24 04:03:56 +00:00
pub const DEFAULT_XMRIG_PATH : & str = " xmrig/xmrig " ;
2023-05-30 14:30:22 +00:00
#[ cfg(target_os = " linux " ) ]
2022-12-18 02:42:30 +00:00
#[ cfg(feature = " distro " ) ]
pub const DEFAULT_P2POOL_PATH : & str = " /usr/bin/p2pool " ;
2023-05-30 14:30:22 +00:00
#[ cfg(target_os = " linux " ) ]
2022-12-18 02:42:30 +00:00
#[ cfg(feature = " distro " ) ]
pub const DEFAULT_XMRIG_PATH : & str = " /usr/bin/xmrig " ;
2022-11-23 04:21:46 +00:00
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- General functions for all [File]'s
// get_file_path() | Return absolute path to OS data path + filename
// read_to_string() | Convert the file at a given path into a [String]
// create_new() | Write a default TOML Struct into the appropriate file (in OS data path)
// into_absolute_path() | Convert relative -> absolute path
2022-11-20 18:31:00 +00:00
pub fn get_gupax_data_path ( ) -> Result < PathBuf , TomlError > {
2022-11-14 02:56:25 +00:00
// Get OS data folder
2022-11-20 18:31:00 +00:00
// Linux | $XDG_DATA_HOME or $HOME/.local/share/gupax | /home/alice/.local/state/gupax
// macOS | $HOME/Library/Application Support/Gupax | /Users/Alice/Library/Application Support/Gupax
// Windows | {FOLDERID_RoamingAppData}\Gupax | C:\Users\Alice\AppData\Roaming\Gupax
match dirs ::data_dir ( ) {
2022-11-14 02:56:25 +00:00
Some ( mut path ) = > {
path . push ( DIRECTORY ) ;
2022-11-21 01:21:47 +00:00
info! ( " OS | Data path ... {} " , path . display ( ) ) ;
2022-11-20 19:20:25 +00:00
create_gupax_dir ( & path ) ? ;
2022-12-26 22:37:45 +00:00
let mut gupax_p2pool_dir = path . clone ( ) ;
gupax_p2pool_dir . push ( GUPAX_P2POOL_API_DIRECTORY ) ;
create_gupax_p2pool_dir ( & gupax_p2pool_dir ) ? ;
2022-11-20 18:31:00 +00:00
Ok ( path )
2022-11-14 02:56:25 +00:00
} ,
2022-11-20 18:31:00 +00:00
None = > { error! ( " OS | Data path ... FAIL " ) ; Err ( TomlError ::Path ( PATH_ERROR . to_string ( ) ) ) } ,
}
}
2022-12-30 14:39:03 +00:00
pub fn set_unix_750_perms ( path : & PathBuf ) -> Result < ( ) , TomlError > {
#[ cfg(target_os = " windows " ) ]
return Ok ( ( ) ) ;
#[ cfg(target_family = " unix " ) ]
match fs ::set_permissions ( path , fs ::Permissions ::from_mode ( 0o750 ) ) {
Ok ( _ ) = > { info! ( " OS | Unix 750 permissions on path [{}] ... OK " , path . display ( ) ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " OS | Unix 750 permissions on path [{}] ... FAIL ... {} " , path . display ( ) , e ) ; Err ( TomlError ::Io ( e ) ) } ,
}
}
pub fn set_unix_660_perms ( path : & PathBuf ) -> Result < ( ) , TomlError > {
#[ cfg(target_os = " windows " ) ]
return Ok ( ( ) ) ;
#[ cfg(target_family = " unix " ) ]
match fs ::set_permissions ( path , fs ::Permissions ::from_mode ( 0o660 ) ) {
Ok ( _ ) = > { info! ( " OS | Unix 660 permissions on path [{}] ... OK " , path . display ( ) ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " OS | Unix 660 permissions on path [{}] ... FAIL ... {} " , path . display ( ) , e ) ; Err ( TomlError ::Io ( e ) ) } ,
}
}
2022-12-26 22:37:45 +00:00
pub fn get_gupax_p2pool_path ( os_data_path : & PathBuf ) -> PathBuf {
let mut gupax_p2pool_dir = os_data_path . clone ( ) ;
gupax_p2pool_dir . push ( GUPAX_P2POOL_API_DIRECTORY ) ;
gupax_p2pool_dir
}
2022-11-20 19:20:25 +00:00
pub fn create_gupax_dir ( path : & PathBuf ) -> Result < ( ) , TomlError > {
2022-12-26 22:37:45 +00:00
// Create Gupax directory
2022-11-20 19:20:25 +00:00
match fs ::create_dir_all ( path ) {
2022-12-30 14:39:03 +00:00
Ok ( _ ) = > info! ( " OS | Create data path ... OK " ) ,
Err ( e ) = > { error! ( " OS | Create data path ... FAIL ... {} " , e ) ; return Err ( TomlError ::Io ( e ) ) } ,
2022-11-20 19:20:25 +00:00
}
2023-01-01 23:57:11 +00:00
set_unix_750_perms ( path )
2022-11-14 02:56:25 +00:00
}
2022-12-26 22:37:45 +00:00
pub fn create_gupax_p2pool_dir ( path : & PathBuf ) -> Result < ( ) , TomlError > {
// Create Gupax directory
match fs ::create_dir_all ( path ) {
2023-01-02 18:32:55 +00:00
Ok ( _ ) = > { info! ( " OS | Create Gupax-P2Pool API path [{}] ... OK " , path . display ( ) ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " OS | Create Gupax-P2Pool API path [{}] ... FAIL ... {} " , path . display ( ) , e ) ; Err ( TomlError ::Io ( e ) ) } ,
2022-12-26 22:37:45 +00:00
}
}
2022-11-14 02:56:25 +00:00
// Convert a [File] path to a [String]
pub fn read_to_string ( file : File , path : & PathBuf ) -> Result < String , TomlError > {
2022-11-24 04:03:56 +00:00
match fs ::read_to_string ( path ) {
2022-11-14 02:56:25 +00:00
Ok ( string ) = > {
info! ( " {:?} | Read ... OK " , file ) ;
Ok ( string )
} ,
Err ( err ) = > {
warn! ( " {:?} | Read ... FAIL " , file ) ;
Err ( TomlError ::Io ( err ) )
} ,
}
}
2022-11-24 04:03:56 +00:00
// Write str to console with [info!] surrounded by "---"
2022-12-03 18:37:57 +00:00
pub fn print_dash ( toml : & str ) {
2022-11-14 02:56:25 +00:00
info! ( " {} " , HORIZONTAL ) ;
for i in toml . lines ( ) { info! ( " {} " , i ) ; }
info! ( " {} " , HORIZONTAL ) ;
}
// Turn relative paths into absolute paths
pub fn into_absolute_path ( path : String ) -> Result < PathBuf , TomlError > {
let path = PathBuf ::from ( path ) ;
if path . is_relative ( ) {
let mut dir = std ::env ::current_exe ( ) ? ;
dir . pop ( ) ;
dir . push ( path ) ;
Ok ( dir )
} else {
Ok ( path )
}
}
//---------------------------------------------------------------------------------------------------- [State] Impl
2022-12-14 03:41:05 +00:00
impl Default for State {
fn default ( ) -> Self {
Self ::new ( )
}
}
2022-11-14 02:56:25 +00:00
impl State {
pub fn new ( ) -> Self {
2023-05-11 20:03:12 +00:00
let max_threads = benri ::threads! ( ) ;
2022-11-24 04:03:56 +00:00
let current_threads = if max_threads = = 1 { 1 } else { max_threads / 2 } ;
2022-11-14 02:56:25 +00:00
Self {
2022-12-27 17:58:46 +00:00
status : Status ::default ( ) ,
2022-12-17 22:17:26 +00:00
gupax : Gupax ::default ( ) ,
p2pool : P2pool ::default ( ) ,
xmrig : Xmrig ::with_threads ( max_threads , current_threads ) ,
2022-12-29 03:03:45 +00:00
version : arc_mut ! ( Version ::default ( ) ) ,
2022-11-14 02:56:25 +00:00
}
}
2022-12-31 00:22:43 +00:00
pub fn update_absolute_path ( & mut self ) -> Result < ( ) , TomlError > {
self . gupax . absolute_p2pool_path = into_absolute_path ( self . gupax . p2pool_path . clone ( ) ) ? ;
self . gupax . absolute_xmrig_path = into_absolute_path ( self . gupax . xmrig_path . clone ( ) ) ? ;
Ok ( ( ) )
}
2022-11-24 04:03:56 +00:00
// Convert [&str] to [State]
pub fn from_str ( string : & str ) -> Result < Self , TomlError > {
match toml ::de ::from_str ( string ) {
2022-11-14 02:56:25 +00:00
Ok ( state ) = > {
2022-11-16 02:19:30 +00:00
info! ( " State | Parse ... OK " ) ;
2022-12-03 18:37:57 +00:00
print_dash ( string ) ;
2022-11-14 02:56:25 +00:00
Ok ( state )
}
Err ( err ) = > {
2022-11-20 02:20:28 +00:00
warn! ( " State | String -> State ... FAIL ... {} " , err ) ;
2022-11-14 02:56:25 +00:00
Err ( TomlError ::Deserialize ( err ) )
} ,
}
}
2022-12-17 22:17:26 +00:00
// Conver [State] to [String]
pub fn to_string ( & self ) -> Result < String , TomlError > {
match toml ::ser ::to_string ( self ) {
Ok ( s ) = > Ok ( s ) ,
Err ( e ) = > { error! ( " State | Couldn't serialize default file: {} " , e ) ; Err ( TomlError ::Serialize ( e ) ) } ,
}
}
2022-11-14 02:56:25 +00:00
// Combination of multiple functions:
// 1. Attempt to read file from path into [String]
// |_ Create a default file if not found
// 2. Deserialize [String] into a proper [Struct]
// |_ Attempt to merge if deserialization fails
2022-11-20 18:31:00 +00:00
pub fn get ( path : & PathBuf ) -> Result < Self , TomlError > {
2022-11-14 02:56:25 +00:00
// Read
let file = File ::State ;
2022-11-24 04:03:56 +00:00
let string = match read_to_string ( file , path ) {
2022-11-14 02:56:25 +00:00
Ok ( string ) = > string ,
// Create
_ = > {
2022-11-20 18:31:00 +00:00
Self ::create_new ( path ) ? ;
2022-11-24 04:03:56 +00:00
match read_to_string ( file , path ) {
2022-11-20 02:20:28 +00:00
Ok ( s ) = > s ,
Err ( e ) = > return Err ( e ) ,
}
2022-11-14 02:56:25 +00:00
} ,
} ;
2022-11-20 02:20:28 +00:00
// Deserialize, attempt merge if failed
2022-11-24 04:03:56 +00:00
match Self ::from_str ( & string ) {
2022-11-20 02:20:28 +00:00
Ok ( s ) = > Ok ( s ) ,
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
Err ( _ ) = > {
2022-11-20 02:20:28 +00:00
warn! ( " State | Attempting merge... " ) ;
2022-12-17 22:17:26 +00:00
match Self ::merge ( & string ) {
Ok ( mut new ) = > { Self ::save ( & mut new , path ) ? ; Ok ( new ) } ,
Err ( e ) = > Err ( e ) ,
}
2022-11-20 02:20:28 +00:00
} ,
}
2022-11-14 02:56:25 +00:00
}
// Completely overwrite current [state.toml]
// with a new default version, and return [Self].
2022-11-20 18:31:00 +00:00
pub fn create_new ( path : & PathBuf ) -> Result < Self , TomlError > {
2022-11-14 02:56:25 +00:00
info! ( " State | Creating new default... " ) ;
let new = Self ::new ( ) ;
2022-12-17 22:17:26 +00:00
let string = Self ::to_string ( & new ) ? ;
2022-12-16 19:33:04 +00:00
fs ::write ( path , string ) ? ;
2022-11-14 02:56:25 +00:00
info! ( " State | Write ... OK " ) ;
Ok ( new )
}
// Save [State] onto disk file [gupax.toml]
2022-11-20 18:31:00 +00:00
pub fn save ( & mut self , path : & PathBuf ) -> Result < ( ) , TomlError > {
2022-11-16 19:07:27 +00:00
info! ( " State | Saving to disk... " ) ;
2022-11-14 02:56:25 +00:00
// Convert path to absolute
self . gupax . absolute_p2pool_path = into_absolute_path ( self . gupax . p2pool_path . clone ( ) ) ? ;
self . gupax . absolute_xmrig_path = into_absolute_path ( self . gupax . xmrig_path . clone ( ) ) ? ;
let string = match toml ::ser ::to_string ( & self ) {
Ok ( string ) = > {
2022-11-16 19:07:27 +00:00
info! ( " State | Parse ... OK " ) ;
2022-12-03 18:37:57 +00:00
print_dash ( & string ) ;
2022-11-14 02:56:25 +00:00
string
} ,
2022-11-16 19:07:27 +00:00
Err ( err ) = > { error! ( " State | Couldn't parse TOML into string ... FAIL " ) ; return Err ( TomlError ::Serialize ( err ) ) } ,
2022-11-14 02:56:25 +00:00
} ;
match fs ::write ( path , string ) {
2022-11-16 19:07:27 +00:00
Ok ( _ ) = > { info! ( " State | Save ... OK " ) ; Ok ( ( ) ) } ,
2022-11-24 04:03:56 +00:00
Err ( err ) = > { error! ( " State | Couldn't overwrite TOML file ... FAIL " ) ; Err ( TomlError ::Io ( err ) ) } ,
2022-11-14 02:56:25 +00:00
}
}
2022-11-20 02:20:28 +00:00
// Take [String] as input, merge it with whatever the current [default] is,
2022-11-14 02:56:25 +00:00
// leaving behind old keys+values and updating [default] with old valid ones.
2022-12-17 22:17:26 +00:00
pub fn merge ( old : & str ) -> Result < Self , TomlError > {
let default = toml ::ser ::to_string ( & Self ::new ( ) ) . unwrap ( ) ;
2022-12-18 01:51:50 +00:00
let new : Self = match Figment ::from ( Toml ::string ( & default ) ) . merge ( Toml ::string ( old ) ) . extract ( ) {
2022-11-20 02:20:28 +00:00
Ok ( new ) = > { info! ( " State | TOML merge ... OK " ) ; new } ,
Err ( err ) = > { error! ( " State | Couldn't merge default + old TOML " ) ; return Err ( TomlError ::Merge ( err ) ) } ,
2022-11-14 02:56:25 +00:00
} ;
Ok ( new )
}
}
//---------------------------------------------------------------------------------------------------- [Node] Impl
impl Node {
pub fn localhost ( ) -> Self {
Self {
ip : " localhost " . to_string ( ) ,
rpc : " 18081 " . to_string ( ) ,
zmq : " 18083 " . to_string ( ) ,
}
}
pub fn new_vec ( ) -> Vec < ( String , Self ) > {
2022-11-24 04:03:56 +00:00
vec! [ ( " Local Monero Node " . to_string ( ) , Self ::localhost ( ) ) ]
2022-11-14 02:56:25 +00:00
}
2023-04-14 16:30:19 +00:00
pub fn new_tuple ( ) -> ( String , Self ) {
( " Local Monero Node " . to_string ( ) , Self ::localhost ( ) )
}
2022-11-14 02:56:25 +00:00
// Convert [String] to [Node] Vec
2022-11-24 04:03:56 +00:00
pub fn from_str_to_vec ( string : & str ) -> Result < Vec < ( String , Self ) > , TomlError > {
let nodes : toml ::map ::Map < String , toml ::Value > = match toml ::de ::from_str ( string ) {
2022-11-14 02:56:25 +00:00
Ok ( map ) = > {
info! ( " Node | Parse ... OK " ) ;
map
}
Err ( err ) = > {
error! ( " Node | String parse ... FAIL ... {} " , err ) ;
return Err ( TomlError ::Deserialize ( err ) )
} ,
} ;
let size = nodes . keys ( ) . len ( ) ;
let mut vec = Vec ::with_capacity ( size ) ;
for ( key , values ) in nodes . iter ( ) {
2022-12-17 00:16:16 +00:00
let ip = match values . get ( " ip " ) {
2022-12-17 03:38:46 +00:00
Some ( ip ) = > match ip . as_str ( ) {
Some ( ip ) = > ip . to_string ( ) ,
None = > { error! ( " Node | [None] at [ip] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [ip] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Node | [None] at [ip] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [ip] parse " ) ) } ,
} ;
let rpc = match values . get ( " rpc " ) {
2022-12-17 03:38:46 +00:00
Some ( rpc ) = > match rpc . as_str ( ) {
Some ( rpc ) = > rpc . to_string ( ) ,
None = > { error! ( " Node | [None] at [rpc] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [rpc] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Node | [None] at [rpc] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [rpc] parse " ) ) } ,
} ;
let zmq = match values . get ( " zmq " ) {
2022-12-17 03:38:46 +00:00
Some ( zmq ) = > match zmq . as_str ( ) {
Some ( zmq ) = > zmq . to_string ( ) ,
None = > { error! ( " Node | [None] at [zmq] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [zmq] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Node | [None] at [zmq] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [zmq] parse " ) ) } ,
} ;
2022-11-16 02:19:30 +00:00
let node = Node {
2022-12-17 00:16:16 +00:00
ip ,
rpc ,
zmq ,
2022-11-16 02:19:30 +00:00
} ;
vec . push ( ( key . clone ( ) , node ) ) ;
2022-11-14 02:56:25 +00:00
}
Ok ( vec )
}
// Convert [Vec<(String, Self)>] into [String]
// that can be written as a proper TOML file
2022-11-24 04:03:56 +00:00
pub fn to_string ( vec : & [ ( String , Self ) ] ) -> Result < String , TomlError > {
2022-11-14 02:56:25 +00:00
let mut toml = String ::new ( ) ;
for ( key , value ) in vec . iter ( ) {
write! (
toml ,
2022-11-19 18:34:46 +00:00
" [ \' {} \' ] \n ip = {:#?} \n rpc = {:#?} \n zmq = {:#?} \n \n " ,
2022-11-14 02:56:25 +00:00
key ,
value . ip ,
value . rpc ,
value . zmq ,
2022-11-20 19:46:43 +00:00
) ? ;
2022-11-14 02:56:25 +00:00
}
2022-11-20 19:46:43 +00:00
Ok ( toml )
2022-11-14 02:56:25 +00:00
}
// Combination of multiple functions:
// 1. Attempt to read file from path into [String]
// |_ Create a default file if not found
// 2. Deserialize [String] into a proper [Struct]
// |_ Attempt to merge if deserialization fails
2022-11-20 18:31:00 +00:00
pub fn get ( path : & PathBuf ) -> Result < Vec < ( String , Self ) > , TomlError > {
2022-11-14 02:56:25 +00:00
// Read
let file = File ::Node ;
2022-11-24 04:03:56 +00:00
let string = match read_to_string ( file , path ) {
2022-11-14 02:56:25 +00:00
Ok ( string ) = > string ,
// Create
_ = > {
2022-11-20 18:31:00 +00:00
Self ::create_new ( path ) ? ;
2022-11-24 04:03:56 +00:00
read_to_string ( file , path ) ?
2022-11-14 02:56:25 +00:00
} ,
} ;
2022-11-20 02:20:28 +00:00
// Deserialize, attempt merge if failed
2022-11-24 04:03:56 +00:00
Self ::from_str_to_vec ( & string )
2022-11-14 02:56:25 +00:00
}
// Completely overwrite current [node.toml]
// with a new default version, and return [Vec<String, Self>].
2022-11-20 18:31:00 +00:00
pub fn create_new ( path : & PathBuf ) -> Result < Vec < ( String , Self ) > , TomlError > {
2022-11-14 02:56:25 +00:00
info! ( " Node | Creating new default... " ) ;
let new = Self ::new_vec ( ) ;
2022-11-20 19:46:43 +00:00
let string = Self ::to_string ( & Self ::new_vec ( ) ) ? ;
2022-12-16 19:33:04 +00:00
fs ::write ( path , string ) ? ;
2022-11-14 02:56:25 +00:00
info! ( " Node | Write ... OK " ) ;
Ok ( new )
}
2022-11-19 18:34:46 +00:00
// Save [Node] onto disk file [node.toml]
2022-11-24 04:03:56 +00:00
pub fn save ( vec : & [ ( String , Self ) ] , path : & PathBuf ) -> Result < ( ) , TomlError > {
2022-12-13 17:44:57 +00:00
info! ( " Node | Saving to disk ... [{}] " , path . display ( ) ) ;
2022-11-20 19:46:43 +00:00
let string = Self ::to_string ( vec ) ? ;
2022-11-19 18:34:46 +00:00
match fs ::write ( path , string ) {
2022-12-13 17:44:57 +00:00
Ok ( _ ) = > { info! ( " Node | Save ... OK " ) ; Ok ( ( ) ) } ,
Err ( err ) = > { error! ( " Node | Couldn't overwrite file " ) ; Err ( TomlError ::Io ( err ) ) } ,
2022-11-19 18:34:46 +00:00
}
}
2022-11-20 02:20:28 +00:00
// pub fn merge(old: &String) -> Result<Self, TomlError> {
// info!("Node | Starting TOML merge...");
2022-11-14 02:56:25 +00:00
// let default = match toml::ser::to_string(&Self::new()) {
2022-11-20 02:20:28 +00:00
// Ok(string) => { info!("Node | Default TOML parse ... OK"); string },
// Err(err) => { error!("Node | Couldn't parse default TOML into string"); return Err(TomlError::Serialize(err)) },
2022-11-14 02:56:25 +00:00
// };
// let mut new: Self = match Figment::new().merge(Toml::string(&old)).merge(Toml::string(&default)).extract() {
2022-11-20 02:20:28 +00:00
// Ok(new) => { info!("Node | TOML merge ... OK"); new },
// Err(err) => { error!("Node | Couldn't merge default + old TOML"); return Err(TomlError::Merge(err)) },
2022-11-14 02:56:25 +00:00
// };
// // Attempt save
// Self::save(&mut new)?;
// Ok(new)
// }
}
2022-11-23 04:21:46 +00:00
//---------------------------------------------------------------------------------------------------- [Pool] impl
impl Pool {
pub fn p2pool ( ) -> Self {
Self {
2022-12-15 16:21:17 +00:00
rig : GUPAX_VERSION_UNDERSCORE . to_string ( ) ,
2022-11-23 04:21:46 +00:00
ip : " localhost " . to_string ( ) ,
port : " 3333 " . to_string ( ) ,
}
}
pub fn new_vec ( ) -> Vec < ( String , Self ) > {
2022-11-24 04:03:56 +00:00
vec! [ ( " Local P2Pool " . to_string ( ) , Self ::p2pool ( ) ) ]
2022-11-23 04:21:46 +00:00
}
2023-04-14 16:30:19 +00:00
pub fn new_tuple ( ) -> ( String , Self ) {
( " Local P2Pool " . to_string ( ) , Self ::p2pool ( ) )
}
2022-11-24 04:03:56 +00:00
pub fn from_str_to_vec ( string : & str ) -> Result < Vec < ( String , Self ) > , TomlError > {
let pools : toml ::map ::Map < String , toml ::Value > = match toml ::de ::from_str ( string ) {
2022-11-23 04:21:46 +00:00
Ok ( map ) = > {
info! ( " Pool | Parse ... OK " ) ;
map
}
Err ( err ) = > {
error! ( " Pool | String parse ... FAIL ... {} " , err ) ;
return Err ( TomlError ::Deserialize ( err ) )
} ,
} ;
let size = pools . keys ( ) . len ( ) ;
let mut vec = Vec ::with_capacity ( size ) ;
2022-12-17 03:38:46 +00:00
// We have to do [.as_str()] -> [.to_string()] to get rid of the \"...\" that gets added on.
2022-11-23 04:21:46 +00:00
for ( key , values ) in pools . iter ( ) {
2022-12-17 00:16:16 +00:00
let rig = match values . get ( " rig " ) {
2022-12-17 03:38:46 +00:00
Some ( rig ) = > match rig . as_str ( ) {
Some ( rig ) = > rig . to_string ( ) ,
None = > { error! ( " Pool | [None] at [rig] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [rig] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Pool | [None] at [rig] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [rig] parse " ) ) } ,
} ;
let ip = match values . get ( " ip " ) {
2022-12-17 03:38:46 +00:00
Some ( ip ) = > match ip . as_str ( ) {
Some ( ip ) = > ip . to_string ( ) ,
None = > { error! ( " Pool | [None] at [ip] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [ip] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Pool | [None] at [ip] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [ip] parse " ) ) } ,
} ;
let port = match values . get ( " port " ) {
2022-12-17 03:38:46 +00:00
Some ( port ) = > match port . as_str ( ) {
Some ( port ) = > port . to_string ( ) ,
None = > { error! ( " Pool | [None] at [port] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [port] parse " ) ) } ,
} ,
2022-12-17 00:16:16 +00:00
None = > { error! ( " Pool | [None] at [port] parse " ) ; return Err ( TomlError ::Parse ( " [None] at [port] parse " ) ) } ,
} ;
2022-11-23 04:21:46 +00:00
let pool = Pool {
2022-12-17 00:16:16 +00:00
rig ,
ip ,
port ,
2022-11-23 04:21:46 +00:00
} ;
vec . push ( ( key . clone ( ) , pool ) ) ;
}
Ok ( vec )
}
2022-11-24 04:03:56 +00:00
pub fn to_string ( vec : & [ ( String , Self ) ] ) -> Result < String , TomlError > {
2022-11-23 04:21:46 +00:00
let mut toml = String ::new ( ) ;
for ( key , value ) in vec . iter ( ) {
write! (
toml ,
" [ \' {} \' ] \n rig = {:#?} \n ip = {:#?} \n port = {:#?} \n \n " ,
key ,
value . rig ,
value . ip ,
value . port ,
) ? ;
}
Ok ( toml )
}
pub fn get ( path : & PathBuf ) -> Result < Vec < ( String , Self ) > , TomlError > {
// Read
let file = File ::Pool ;
2022-11-24 04:03:56 +00:00
let string = match read_to_string ( file , path ) {
2022-11-23 04:21:46 +00:00
Ok ( string ) = > string ,
// Create
_ = > {
Self ::create_new ( path ) ? ;
2022-11-24 04:03:56 +00:00
read_to_string ( file , path ) ?
2022-11-23 04:21:46 +00:00
} ,
} ;
// Deserialize
2022-11-24 04:03:56 +00:00
Self ::from_str_to_vec ( & string )
2022-11-23 04:21:46 +00:00
}
pub fn create_new ( path : & PathBuf ) -> Result < Vec < ( String , Self ) > , TomlError > {
info! ( " Pool | Creating new default... " ) ;
let new = Self ::new_vec ( ) ;
let string = Self ::to_string ( & Self ::new_vec ( ) ) ? ;
2022-12-16 19:33:04 +00:00
fs ::write ( path , string ) ? ;
2022-11-23 04:21:46 +00:00
info! ( " Pool | Write ... OK " ) ;
Ok ( new )
}
2022-11-24 04:03:56 +00:00
pub fn save ( vec : & [ ( String , Self ) ] , path : & PathBuf ) -> Result < ( ) , TomlError > {
2022-12-13 17:44:57 +00:00
info! ( " Pool | Saving to disk ... [{}] " , path . display ( ) ) ;
2022-11-23 04:21:46 +00:00
let string = Self ::to_string ( vec ) ? ;
match fs ::write ( path , string ) {
2022-12-13 17:44:57 +00:00
Ok ( _ ) = > { info! ( " Pool | Save ... OK " ) ; Ok ( ( ) ) } ,
Err ( err ) = > { error! ( " Pool | Couldn't overwrite file " ) ; Err ( TomlError ::Io ( err ) ) } ,
2022-11-23 04:21:46 +00:00
}
}
}
2022-12-26 22:37:45 +00:00
//---------------------------------------------------------------------------------------------------- Gupax-P2Pool API
2022-12-28 21:04:26 +00:00
#[ derive(Clone,Debug) ]
2022-12-26 22:37:45 +00:00
pub struct GupaxP2poolApi {
2022-12-29 17:12:12 +00:00
pub log : String , // Log file only containing full payout lines
2022-12-29 22:03:29 +00:00
pub log_rev : String , // Same as above but reversed based off lines
2022-12-29 17:12:12 +00:00
pub payout : HumanNumber , // Human-friendly display of payout count
pub payout_u64 : u64 , // [u64] version of above
pub payout_ord : PayoutOrd , // Ordered Vec of payouts, see [PayoutOrd]
2022-12-29 22:03:29 +00:00
pub payout_low : String , // A pre-allocated/computed [String] of the above Vec from low payout to high
pub payout_high : String , // Same as above but high -> low
2022-12-29 17:12:12 +00:00
pub xmr : AtomicUnit , // XMR stored as atomic units
pub path_log : PathBuf , // Path to [log]
pub path_payout : PathBuf , // Path to [payout]
pub path_xmr : PathBuf , // Path to [xmr]
2022-12-26 22:37:45 +00:00
}
impl Default for GupaxP2poolApi { fn default ( ) -> Self { Self ::new ( ) } }
impl GupaxP2poolApi {
2022-12-31 18:47:41 +00:00
//---------------------------------------------------------------------------------------------------- Init, these pretty much only get called once
2022-12-26 22:37:45 +00:00
pub fn new ( ) -> Self {
Self {
2022-12-29 17:12:12 +00:00
log : String ::new ( ) ,
2022-12-29 22:03:29 +00:00
log_rev : String ::new ( ) ,
2022-12-26 22:37:45 +00:00
payout : HumanNumber ::unknown ( ) ,
2022-12-29 17:12:12 +00:00
payout_u64 : 0 ,
2022-12-28 21:04:26 +00:00
payout_ord : PayoutOrd ::new ( ) ,
2022-12-29 22:03:29 +00:00
payout_low : String ::new ( ) ,
payout_high : String ::new ( ) ,
2022-12-29 17:12:12 +00:00
xmr : AtomicUnit ::new ( ) ,
2022-12-28 21:04:26 +00:00
path_xmr : PathBuf ::new ( ) ,
2022-12-29 17:12:12 +00:00
path_payout : PathBuf ::new ( ) ,
path_log : PathBuf ::new ( ) ,
2022-12-26 22:37:45 +00:00
}
}
pub fn fill_paths ( & mut self , gupax_p2pool_dir : & PathBuf ) {
2022-12-29 17:12:12 +00:00
let mut path_log = gupax_p2pool_dir . clone ( ) ;
let mut path_payout = gupax_p2pool_dir . clone ( ) ;
let mut path_xmr = gupax_p2pool_dir . clone ( ) ;
path_log . push ( GUPAX_P2POOL_API_LOG ) ;
path_payout . push ( GUPAX_P2POOL_API_PAYOUT ) ;
path_xmr . push ( GUPAX_P2POOL_API_XMR ) ;
2022-12-26 22:37:45 +00:00
* self = Self {
2022-12-29 17:12:12 +00:00
path_log ,
path_payout ,
2022-12-28 21:04:26 +00:00
path_xmr ,
2022-12-26 22:37:45 +00:00
.. std ::mem ::take ( self )
} ;
}
pub fn create_all_files ( gupax_p2pool_dir : & PathBuf ) -> Result < ( ) , TomlError > {
use std ::io ::Write ;
for file in GUPAX_P2POOL_API_FILE_ARRAY {
let mut path = gupax_p2pool_dir . clone ( ) ;
path . push ( file ) ;
if path . exists ( ) {
info! ( " GupaxP2poolApi | [{}] already exists, skipping... " , path . display ( ) ) ;
continue
}
match std ::fs ::File ::create ( & path ) {
Ok ( mut f ) = > {
match file {
2022-12-29 17:12:12 +00:00
GUPAX_P2POOL_API_PAYOUT | GUPAX_P2POOL_API_XMR = > writeln! ( f , " 0 " ) ? ,
2022-12-26 22:37:45 +00:00
_ = > ( ) ,
}
info! ( " GupaxP2poolApi | [{}] create ... OK " , path . display ( ) ) ;
} ,
Err ( e ) = > { warn! ( " GupaxP2poolApi | [{}] create ... FAIL: {} " , path . display ( ) , e ) ; return Err ( TomlError ::Io ( e ) ) } ,
}
}
Ok ( ( ) )
}
2022-12-31 18:47:41 +00:00
pub fn read_all_files_and_update ( & mut self ) -> Result < ( ) , TomlError > {
let payout_u64 = match read_to_string ( File ::Payout , & self . path_payout ) ? . trim ( ) . parse ::< u64 > ( ) {
Ok ( o ) = > o ,
Err ( e ) = > { warn! ( " GupaxP2poolApi | [payout] parse error: {} " , e ) ; return Err ( TomlError ::Parse ( " payout " ) ) }
} ;
let xmr = match read_to_string ( File ::Xmr , & self . path_xmr ) ? . trim ( ) . parse ::< u64 > ( ) {
Ok ( o ) = > AtomicUnit ::from_u64 ( o ) ,
Err ( e ) = > { warn! ( " GupaxP2poolApi | [xmr] parse error: {} " , e ) ; return Err ( TomlError ::Parse ( " xmr " ) ) }
} ;
let payout = HumanNumber ::from_u64 ( payout_u64 ) ;
let log = read_to_string ( File ::Log , & self . path_log ) ? ;
self . payout_ord . update_from_payout_log ( & log ) ;
self . update_payout_strings ( ) ;
* self = Self {
log ,
payout ,
payout_u64 ,
xmr ,
.. std ::mem ::take ( self )
} ;
self . update_log_rev ( ) ;
Ok ( ( ) )
}
2023-01-02 18:32:55 +00:00
// Completely delete the [p2pool] folder and create defaults.
pub fn create_new ( path : & PathBuf ) -> Result < ( ) , TomlError > {
info! ( " GupaxP2poolApi | Deleting old folder at [{}]... " , path . display ( ) ) ;
std ::fs ::remove_dir_all ( & path ) ? ;
info! ( " GupaxP2poolApi | Creating new default folder at [{}]... " , path . display ( ) ) ;
create_gupax_p2pool_dir ( & path ) ? ;
Self ::create_all_files ( & path ) ? ;
Ok ( ( ) )
}
2022-12-31 18:47:41 +00:00
//---------------------------------------------------------------------------------------------------- Live, functions that actually update/write live stats
2022-12-29 22:03:29 +00:00
pub fn update_log_rev ( & mut self ) {
let mut log_rev = String ::with_capacity ( self . log . len ( ) ) ;
for line in self . log . lines ( ) . rev ( ) {
log_rev . push_str ( line ) ;
2023-01-01 23:57:11 +00:00
log_rev . push ( '\n' ) ;
2022-12-29 22:03:29 +00:00
}
self . log_rev = log_rev ;
}
2022-12-31 18:47:41 +00:00
pub fn format_payout ( date : & str , atomic_unit : & AtomicUnit , block : & HumanNumber ) -> String {
format! ( " {} | {} XMR | Block {} " , date , atomic_unit , block )
}
pub fn append_log ( & mut self , formatted_log_line : & str ) {
self . log . push_str ( formatted_log_line ) ;
2023-01-01 23:57:11 +00:00
self . log . push ( '\n' ) ;
2022-12-31 18:47:41 +00:00
}
pub fn append_head_log_rev ( & mut self , formatted_log_line : & str ) {
self . log_rev = format! ( " {} \n {} " , formatted_log_line , self . log_rev ) ;
2022-12-29 22:03:29 +00:00
}
pub fn update_payout_low ( & mut self ) {
self . payout_ord . sort_payout_low_to_high ( ) ;
self . payout_low = self . payout_ord . to_string ( ) ;
}
pub fn update_payout_high ( & mut self ) {
self . payout_ord . sort_payout_high_to_low ( ) ;
self . payout_high = self . payout_ord . to_string ( ) ;
}
pub fn update_payout_strings ( & mut self ) {
self . update_payout_low ( ) ;
self . update_payout_high ( ) ;
}
2022-12-31 18:47:41 +00:00
// Takes the (date, atomic_unit, block) and updates [self] and the [PayoutOrd]
pub fn add_payout ( & mut self , formatted_log_line : & str , date : String , atomic_unit : AtomicUnit , block : HumanNumber ) {
self . append_log ( formatted_log_line ) ;
self . append_head_log_rev ( formatted_log_line ) ;
2022-12-29 17:12:12 +00:00
self . payout_u64 + = 1 ;
self . payout = HumanNumber ::from_u64 ( self . payout_u64 ) ;
self . xmr = self . xmr . add_self ( atomic_unit ) ;
self . payout_ord . push ( date , atomic_unit , block ) ;
2022-12-29 22:03:29 +00:00
self . update_payout_strings ( ) ;
2022-12-28 21:04:26 +00:00
}
2022-12-31 18:47:41 +00:00
pub fn write_to_all_files ( & self , formatted_log_line : & str ) -> Result < ( ) , TomlError > {
2022-12-29 17:12:12 +00:00
Self ::disk_overwrite ( & self . payout_u64 . to_string ( ) , & self . path_payout ) ? ;
Self ::disk_overwrite ( & self . xmr . to_string ( ) , & self . path_xmr ) ? ;
2022-12-31 18:47:41 +00:00
Self ::disk_append ( formatted_log_line , & self . path_log ) ? ;
2022-12-26 22:37:45 +00:00
Ok ( ( ) )
}
2022-12-31 18:47:41 +00:00
pub fn disk_append ( formatted_log_line : & str , path : & PathBuf ) -> Result < ( ) , TomlError > {
2022-12-28 21:04:26 +00:00
use std ::io ::Write ;
2022-12-29 17:12:12 +00:00
let mut file = match fs ::OpenOptions ::new ( ) . append ( true ) . create ( true ) . open ( path ) {
2022-12-28 21:04:26 +00:00
Ok ( f ) = > f ,
Err ( e ) = > { error! ( " GupaxP2poolApi | Append [{}] ... FAIL: {} " , path . display ( ) , e ) ; return Err ( TomlError ::Io ( e ) ) } ,
} ;
2022-12-31 18:47:41 +00:00
match writeln! ( file , " {} " , formatted_log_line ) {
2022-12-28 21:04:26 +00:00
Ok ( _ ) = > { debug! ( " GupaxP2poolApi | Append [{}] ... OK " , path . display ( ) ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " GupaxP2poolApi | Append [{}] ... FAIL: {} " , path . display ( ) , e ) ; Err ( TomlError ::Io ( e ) ) } ,
}
}
pub fn disk_overwrite ( string : & str , path : & PathBuf ) -> Result < ( ) , TomlError > {
use std ::io ::Write ;
2022-12-29 17:12:12 +00:00
let mut file = match fs ::OpenOptions ::new ( ) . write ( true ) . truncate ( true ) . create ( true ) . open ( path ) {
2022-12-28 21:04:26 +00:00
Ok ( f ) = > f ,
Err ( e ) = > { error! ( " GupaxP2poolApi | Overwrite [{}] ... FAIL: {} " , path . display ( ) , e ) ; return Err ( TomlError ::Io ( e ) ) } ,
} ;
match writeln! ( file , " {} " , string ) {
Ok ( _ ) = > { debug! ( " GupaxP2poolApi | Overwrite [{}] ... OK " , path . display ( ) ) ; Ok ( ( ) ) } ,
Err ( e ) = > { error! ( " GupaxP2poolApi | Overwrite [{}] ... FAIL: {} " , path . display ( ) , e ) ; Err ( TomlError ::Io ( e ) ) } ,
2022-12-26 22:37:45 +00:00
}
}
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- Custom Error [TomlError]
2022-11-20 02:20:28 +00:00
#[ derive(Debug) ]
pub enum TomlError {
Io ( std ::io ::Error ) ,
Path ( String ) ,
Serialize ( toml ::ser ::Error ) ,
Deserialize ( toml ::de ::Error ) ,
Merge ( figment ::Error ) ,
2022-11-20 19:46:43 +00:00
Format ( std ::fmt ::Error ) ,
2022-12-17 00:16:16 +00:00
Parse ( & 'static str ) ,
2022-11-20 02:20:28 +00:00
}
2022-11-14 02:56:25 +00:00
impl Display for TomlError {
2022-11-20 02:20:28 +00:00
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
2022-11-14 02:56:25 +00:00
use TomlError ::* ;
match self {
2022-11-20 02:20:28 +00:00
Io ( err ) = > write! ( f , " {}: IO | {} " , ERROR , err ) ,
Path ( err ) = > write! ( f , " {}: Path | {} " , ERROR , err ) ,
Serialize ( err ) = > write! ( f , " {}: Serialize | {} " , ERROR , err ) ,
Deserialize ( err ) = > write! ( f , " {}: Deserialize | {} " , ERROR , err ) ,
Merge ( err ) = > write! ( f , " {}: Merge | {} " , ERROR , err ) ,
2022-11-20 19:46:43 +00:00
Format ( err ) = > write! ( f , " {}: Format | {} " , ERROR , err ) ,
2022-12-17 00:16:16 +00:00
Parse ( err ) = > write! ( f , " {}: Parse | {} " , ERROR , err ) ,
2022-11-14 02:56:25 +00:00
}
}
}
impl From < std ::io ::Error > for TomlError {
fn from ( err : std ::io ::Error ) -> Self {
TomlError ::Io ( err )
}
}
2022-11-20 19:46:43 +00:00
impl From < std ::fmt ::Error > for TomlError {
fn from ( err : std ::fmt ::Error ) -> Self {
TomlError ::Format ( err )
}
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- [File] Enum (for matching which file)
#[ derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub enum File {
2022-12-26 22:37:45 +00:00
// State files
State , // state.toml | Gupax state
Node , // node.toml | P2Pool manual node selector
Pool , // pool.toml | XMRig manual pool selector
// Gupax-P2Pool API
2022-12-29 17:12:12 +00:00
Log , // log | Raw log lines of P2Pool payouts received
Payout , // payout | Single [u64] representing total payouts
Xmr , // xmr | Single [u64] representing total XMR mined in atomic units
2022-11-14 02:56:25 +00:00
}
2022-12-27 17:58:46 +00:00
//---------------------------------------------------------------------------------------------------- [Submenu] enum for [Status] tab
#[ derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub enum Submenu {
Processes ,
P2pool ,
2023-03-17 20:12:06 +00:00
Benchmarks ,
2022-12-27 17:58:46 +00:00
}
impl Default for Submenu {
fn default ( ) -> Self {
Self ::Processes
}
}
impl Display for Submenu {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
use Submenu ::* ;
match self {
P2pool = > write! ( f , " P2Pool " ) ,
2023-01-01 23:57:11 +00:00
_ = > write! ( f , " {:?} " , self ) ,
2022-12-27 17:58:46 +00:00
}
}
}
2022-12-29 22:03:29 +00:00
//---------------------------------------------------------------------------------------------------- [PayoutView] enum for [Status/P2Pool] tab
// The enum buttons for selecting which "view" to sort the payout log in.
#[ derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub enum PayoutView {
Latest , // Shows the most recent logs first
Oldest , // Shows the oldest logs first
Biggest , // Shows highest to lowest payouts
Smallest , // Shows lowest to highest payouts
}
impl PayoutView {
fn new ( ) -> Self {
Self ::Latest
}
}
impl Default for PayoutView {
fn default ( ) -> Self {
Self ::new ( )
}
}
impl Display for PayoutView {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
2023-01-01 23:57:11 +00:00
write! ( f , " {:?} " , self )
2022-12-29 22:03:29 +00:00
}
}
2022-12-27 22:27:10 +00:00
//---------------------------------------------------------------------------------------------------- [Hash] enum for [Status/P2Pool]
2022-12-27 17:58:46 +00:00
#[ derive(Clone,Copy,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub enum Hash {
Hash ,
Kilo ,
Mega ,
Giga ,
}
impl Default for Hash {
fn default ( ) -> Self {
Self ::Hash
}
}
impl Hash {
2022-12-31 00:22:43 +00:00
pub fn convert_to_hash ( f : f64 , from : Self ) -> f64 {
match from {
Self ::Hash = > f ,
Self ::Kilo = > f * 1_000.0 ,
Self ::Mega = > f * 1_000_000.0 ,
Self ::Giga = > f * 1_000_000_000.0 ,
}
}
2022-12-27 17:58:46 +00:00
pub fn convert ( f : f64 , og : Self , new : Self ) -> f64 {
match og {
Self ::Hash = > {
match new {
Self ::Hash = > f ,
Self ::Kilo = > f / 1_000.0 ,
Self ::Mega = > f / 1_000_000.0 ,
Self ::Giga = > f / 1_000_000_000.0 ,
}
} ,
Self ::Kilo = > {
match new {
Self ::Hash = > f * 1_000.0 ,
Self ::Kilo = > f ,
Self ::Mega = > f / 1_000.0 ,
Self ::Giga = > f / 1_000_000.0 ,
}
} ,
Self ::Mega = > {
match new {
Self ::Hash = > f * 1_000_000.0 ,
Self ::Kilo = > f * 1_000.0 ,
Self ::Mega = > f ,
Self ::Giga = > f / 1_000.0 ,
}
} ,
Self ::Giga = > {
match new {
Self ::Hash = > f * 1_000_000_000.0 ,
Self ::Kilo = > f * 1_000_000.0 ,
Self ::Mega = > f * 1_000.0 ,
Self ::Giga = > f ,
}
} ,
}
}
}
impl Display for Hash {
fn fmt ( & self , f : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
match self {
2023-01-01 23:57:11 +00:00
Hash ::Hash = > write! ( f , " Hash " ) ,
_ = > write! ( f , " {:?}hash " , self ) ,
2022-12-27 17:58:46 +00:00
}
}
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- [Node] Struct
#[ derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub struct Node {
pub ip : String ,
pub rpc : String ,
pub zmq : String ,
}
2022-11-23 04:21:46 +00:00
//---------------------------------------------------------------------------------------------------- [Pool] Struct
#[ derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub struct Pool {
pub rig : String ,
pub ip : String ,
pub port : String ,
}
2022-11-14 02:56:25 +00:00
//---------------------------------------------------------------------------------------------------- [State] Struct
#[ derive(Clone,Debug,Deserialize,Serialize) ]
pub struct State {
2022-12-27 17:58:46 +00:00
pub status : Status ,
2022-11-14 02:56:25 +00:00
pub gupax : Gupax ,
pub p2pool : P2pool ,
pub xmrig : Xmrig ,
pub version : Arc < Mutex < Version > > ,
}
2022-12-27 17:58:46 +00:00
#[ derive(Clone,PartialEq,Debug,Deserialize,Serialize) ]
pub struct Status {
pub submenu : Submenu ,
2022-12-29 22:03:29 +00:00
pub payout_view : PayoutView ,
2022-12-27 17:58:46 +00:00
pub monero_enabled : bool ,
pub manual_hash : bool ,
pub hashrate : f64 ,
pub hash_metric : Hash ,
}
2022-11-14 02:56:25 +00:00
#[ derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub struct Gupax {
2022-11-23 04:21:46 +00:00
pub simple : bool ,
2022-11-14 02:56:25 +00:00
pub auto_update : bool ,
2022-12-11 04:06:24 +00:00
pub auto_p2pool : bool ,
pub auto_xmrig : bool ,
2022-12-27 17:58:46 +00:00
// pub auto_monero: bool,
2022-11-14 02:56:25 +00:00
pub ask_before_quit : bool ,
pub save_before_quit : bool ,
pub update_via_tor : bool ,
pub p2pool_path : String ,
pub xmrig_path : String ,
pub absolute_p2pool_path : PathBuf ,
pub absolute_xmrig_path : PathBuf ,
2022-11-23 04:21:46 +00:00
pub selected_width : u16 ,
pub selected_height : u16 ,
2022-12-06 03:33:35 +00:00
pub tab : Tab ,
2022-11-23 04:21:46 +00:00
pub ratio : Ratio ,
2022-11-14 02:56:25 +00:00
}
#[ derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub struct P2pool {
pub simple : bool ,
pub mini : bool ,
2022-12-15 16:21:17 +00:00
pub auto_ping : bool ,
2022-11-14 02:56:25 +00:00
pub auto_select : bool ,
pub out_peers : u16 ,
pub in_peers : u16 ,
pub log_level : u8 ,
2023-01-26 03:34:51 +00:00
pub node : String ,
2022-11-14 02:56:25 +00:00
pub arguments : String ,
pub address : String ,
pub name : String ,
pub ip : String ,
pub rpc : String ,
pub zmq : String ,
cargo/tor/p2pool: clean deps, warn macos arti, fix node overflow
Cargo: Cleanup unused dependencies, enable some build optimizations
Tor: Arti doesn't seem to work on macOS
Even a bare Arti+Hyper request doesn't seem to work, so it's
probably not something to do with Gupax. A lot of issues only
seem to popup in a VM (OpenGL, TLS) even though on bare metal
Gupax runs fine, so Tor might work fine on real macOS but I don't
have real macOS to test it. VM macOS can't create a circuit, so,
disable by default and add a warning that it's unstable.
P2Pool: Let selected_index start at 0, and only +1 when printing
to the user, this makes the overflow math when adding/deleting a
lot more simple because selected_index will match the actual index
of the node vector
2022-11-21 22:16:31 +00:00
pub selected_index : usize ,
2022-11-14 02:56:25 +00:00
pub selected_name : String ,
pub selected_ip : String ,
pub selected_rpc : String ,
pub selected_zmq : String ,
}
#[ derive(Clone,Eq,PartialEq,Debug,Deserialize,Serialize) ]
pub struct Xmrig {
pub simple : bool ,
2022-11-23 04:21:46 +00:00
pub pause : u8 ,
2022-12-03 18:37:57 +00:00
pub simple_rig : String ,
pub arguments : String ,
2022-11-14 02:56:25 +00:00
pub tls : bool ,
pub keepalive : bool ,
pub max_threads : usize ,
pub current_threads : usize ,
pub address : String ,
2022-11-23 04:21:46 +00:00
pub api_ip : String ,
pub api_port : String ,
pub name : String ,
pub rig : String ,
pub ip : String ,
pub port : String ,
pub selected_index : usize ,
pub selected_name : String ,
pub selected_rig : String ,
pub selected_ip : String ,
pub selected_port : String ,
2022-11-14 02:56:25 +00:00
}
#[ derive(Clone,Debug,Deserialize,Serialize) ]
pub struct Version {
2022-11-19 14:39:26 +00:00
pub gupax : String ,
pub p2pool : String ,
pub xmrig : String ,
2022-11-14 02:56:25 +00:00
}
2022-12-17 22:17:26 +00:00
//---------------------------------------------------------------------------------------------------- [State] Defaults
2022-12-27 17:58:46 +00:00
impl Default for Status {
fn default ( ) -> Self {
Self {
submenu : Submenu ::default ( ) ,
2022-12-29 22:03:29 +00:00
payout_view : PayoutView ::default ( ) ,
2022-12-27 17:58:46 +00:00
monero_enabled : false ,
manual_hash : false ,
2022-12-31 00:22:43 +00:00
hashrate : 1.0 ,
2022-12-27 17:58:46 +00:00
hash_metric : Hash ::default ( ) ,
}
}
}
2022-12-17 22:17:26 +00:00
impl Default for Gupax {
fn default ( ) -> Self {
Self {
simple : true ,
auto_update : true ,
auto_p2pool : false ,
auto_xmrig : false ,
ask_before_quit : true ,
save_before_quit : true ,
#[ cfg(not(target_os = " macos " )) ]
update_via_tor : true ,
#[ cfg(target_os = " macos " ) ] // Arti library has issues on macOS
update_via_tor : false ,
p2pool_path : DEFAULT_P2POOL_PATH . to_string ( ) ,
xmrig_path : DEFAULT_XMRIG_PATH . to_string ( ) ,
absolute_p2pool_path : into_absolute_path ( DEFAULT_P2POOL_PATH . to_string ( ) ) . unwrap ( ) ,
absolute_xmrig_path : into_absolute_path ( DEFAULT_XMRIG_PATH . to_string ( ) ) . unwrap ( ) ,
selected_width : APP_DEFAULT_WIDTH as u16 ,
selected_height : APP_DEFAULT_HEIGHT as u16 ,
ratio : Ratio ::Width ,
tab : Tab ::About ,
}
}
}
2022-12-31 00:22:43 +00:00
2022-12-17 22:17:26 +00:00
impl Default for P2pool {
fn default ( ) -> Self {
Self {
simple : true ,
mini : true ,
auto_ping : true ,
auto_select : true ,
out_peers : 10 ,
in_peers : 10 ,
log_level : 3 ,
2023-01-26 03:34:51 +00:00
node : crate ::RemoteNode ::new ( ) . to_string ( ) ,
2022-12-17 22:17:26 +00:00
arguments : String ::new ( ) ,
address : String ::with_capacity ( 96 ) ,
name : " Local Monero Node " . to_string ( ) ,
ip : " localhost " . to_string ( ) ,
rpc : " 18081 " . to_string ( ) ,
zmq : " 18083 " . to_string ( ) ,
selected_index : 0 ,
selected_name : " Local Monero Node " . to_string ( ) ,
selected_ip : " localhost " . to_string ( ) ,
selected_rpc : " 18081 " . to_string ( ) ,
selected_zmq : " 18083 " . to_string ( ) ,
}
}
}
impl Xmrig {
fn with_threads ( max_threads : usize , current_threads : usize ) -> Self {
2022-12-18 01:51:50 +00:00
let xmrig = Self ::default ( ) ;
2022-12-17 22:17:26 +00:00
Self {
max_threads ,
current_threads ,
.. xmrig
}
}
}
impl Default for Xmrig {
fn default ( ) -> Self {
Self {
simple : true ,
pause : 0 ,
simple_rig : String ::with_capacity ( 30 ) ,
arguments : String ::with_capacity ( 300 ) ,
address : String ::with_capacity ( 96 ) ,
name : " Local P2Pool " . to_string ( ) ,
rig : GUPAX_VERSION_UNDERSCORE . to_string ( ) ,
ip : " localhost " . to_string ( ) ,
port : " 3333 " . to_string ( ) ,
selected_index : 0 ,
selected_name : " Local P2Pool " . to_string ( ) ,
selected_ip : " localhost " . to_string ( ) ,
selected_rig : GUPAX_VERSION_UNDERSCORE . to_string ( ) ,
selected_port : " 3333 " . to_string ( ) ,
api_ip : " localhost " . to_string ( ) ,
api_port : " 18088 " . to_string ( ) ,
tls : false ,
keepalive : false ,
current_threads : 1 ,
max_threads : 1 ,
}
}
}
impl Default for Version {
fn default ( ) -> Self {
Self {
gupax : GUPAX_VERSION . to_string ( ) ,
p2pool : P2POOL_VERSION . to_string ( ) ,
xmrig : XMRIG_VERSION . to_string ( ) ,
}
}
}
//---------------------------------------------------------------------------------------------------- TESTS
#[ cfg(test) ]
mod test {
#[ test ]
fn serde_default_state ( ) {
let state = crate ::State ::new ( ) ;
let string = crate ::State ::to_string ( & state ) . unwrap ( ) ;
crate ::State ::from_str ( & string ) . unwrap ( ) ;
}
#[ test ]
fn serde_default_node ( ) {
let node = crate ::Node ::new_vec ( ) ;
let string = crate ::Node ::to_string ( & node ) . unwrap ( ) ;
crate ::Node ::from_str_to_vec ( & string ) . unwrap ( ) ;
}
#[ test ]
fn serde_default_pool ( ) {
let pool = crate ::Pool ::new_vec ( ) ;
let string = crate ::Pool ::to_string ( & pool ) . unwrap ( ) ;
crate ::Pool ::from_str_to_vec ( & string ) . unwrap ( ) ;
}
#[ test ]
fn serde_custom_state ( ) {
let state = r #"
[ gupax ]
simple = true
auto_update = true
auto_p2pool = false
auto_xmrig = false
ask_before_quit = true
save_before_quit = true
update_via_tor = true
p2pool_path = " p2pool/p2pool "
xmrig_path = " xmrig/xmrig "
absolute_p2pool_path = " /home/hinto/p2pool/p2pool "
absolute_xmrig_path = " /home/hinto/xmrig/xmrig "
selected_width = 1280
selected_height = 960
tab = " About "
ratio = " Width "
2022-12-27 17:58:46 +00:00
[ status ]
submenu = " P2pool "
2022-12-30 14:39:03 +00:00
payout_view = " Oldest "
2022-12-27 17:58:46 +00:00
monero_enabled = true
manual_hash = false
hashrate = 1241.23
hash_metric = " Hash "
2022-12-17 22:17:26 +00:00
[ p2pool ]
simple = true
mini = true
auto_ping = true
auto_select = true
out_peers = 10
in_peers = 450
log_level = 3
node = " Seth "
arguments = " "
address = " 44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW "
name = " Local Monero Node "
ip = " 192.168.1.123 "
rpc = " 18089 "
zmq = " 18083 "
selected_index = 0
selected_name = " Local Monero Node "
selected_ip = " 192.168.1.123 "
selected_rpc = " 18089 "
selected_zmq = " 18083 "
[ xmrig ]
simple = true
pause = 0
simple_rig = " "
arguments = " "
tls = false
keepalive = false
max_threads = 32
current_threads = 16
address = " "
api_ip = " localhost "
api_port = " 18088 "
name = " linux "
rig = " Gupax "
ip = " 192.168.1.122 "
port = " 3333 "
selected_index = 1
selected_name = " linux "
selected_rig = " Gupax "
selected_ip = " 192.168.1.122 "
selected_port = " 3333 "
[ version ]
gupax = " v1.0.0 "
p2pool = " v2.5 "
xmrig = " v6.18.0 "
" #;
let state = crate ::State ::from_str ( state ) . unwrap ( ) ;
crate ::State ::to_string ( & state ) . unwrap ( ) ;
}
#[ test ]
fn serde_custom_node ( ) {
let node = r #"
[ ' Local Monero Node ' ]
ip = " localhost "
rpc = " 18081 "
zmq = " 18083 "
[ ' asdf - _ . . _123 ' ]
ip = " localhost "
rpc = " 11 "
zmq = " 1234 "
[ ' aaa bbb ' ]
ip = " 192.168.2.333 "
rpc = " 1 "
zmq = " 65535 "
" #;
let node = crate ::Node ::from_str_to_vec ( node ) . unwrap ( ) ;
crate ::Node ::to_string ( & node ) . unwrap ( ) ;
}
#[ test ]
fn serde_custom_pool ( ) {
let pool = r #"
[ ' Local P2Pool ' ]
rig = " Gupax_v1.0.0 "
ip = " localhost "
port = " 3333 "
[ ' aaa xx .. - ' ]
rig = " Gupax "
ip = " 192.168.22.22 "
port = " 1 "
[ ' a ' ]
rig = " Gupax_v1.0.0 "
ip = " 127.0.0.1 "
port = " 65535 "
" #;
let pool = crate ::Pool ::from_str_to_vec ( pool ) . unwrap ( ) ;
crate ::Pool ::to_string ( & pool ) . unwrap ( ) ;
}
// Make sure we keep the user's old values that are still
// valid but discard the ones that don't exist anymore.
#[ test ]
fn merge_state ( ) {
let bad_state = r #"
[ gupax ]
SETTING_THAT_DOESNT_EXIST_ANYMORE = 123123
simple = false
auto_update = true
auto_p2pool = false
auto_xmrig = false
ask_before_quit = true
save_before_quit = true
update_via_tor = true
p2pool_path = " p2pool/p2pool "
xmrig_path = " xmrig/xmrig "
absolute_p2pool_path = " "
absolute_xmrig_path = " "
selected_width = 0
selected_height = 0
tab = " About "
ratio = " Width "
[ p2pool ]
SETTING_THAT_DOESNT_EXIST_ANYMORE = " String "
simple = true
mini = true
auto_ping = true
auto_select = true
out_peers = 10
in_peers = 450
log_level = 6
node = " Seth "
arguments = " "
address = " 44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW "
name = " Local Monero Node "
ip = " localhost "
rpc = " 18081 "
zmq = " 18083 "
selected_index = 0
selected_name = " Local Monero Node "
selected_ip = " localhost "
selected_rpc = " 18081 "
selected_zmq = " 18083 "
[ xmrig ]
SETTING_THAT_DOESNT_EXIST_ANYMORE = true
simple = true
pause = 0
simple_rig = " "
arguments = " "
tls = false
keepalive = false
max_threads = 32
current_threads = 16
address = " "
api_ip = " localhost "
api_port = " 18088 "
name = " Local P2Pool "
rig = " Gupax_v1.0.0 "
ip = " localhost "
port = " 3333 "
selected_index = 0
selected_name = " Local P2Pool "
selected_rig = " Gupax_v1.0.0 "
selected_ip = " localhost "
selected_port = " 3333 "
[ version ]
gupax = " v1.0.0 "
p2pool = " v2.5 "
xmrig = " v6.18.0 "
" #.to_string();
let merged_state = crate ::State ::merge ( & bad_state ) . unwrap ( ) ;
let merged_state = crate ::State ::to_string ( & merged_state ) . unwrap ( ) ;
println! ( " {} " , merged_state ) ;
assert! ( merged_state . contains ( " simple = false " ) ) ;
assert! ( merged_state . contains ( " in_peers = 450 " ) ) ;
assert! ( merged_state . contains ( " log_level = 6 " ) ) ;
assert! ( merged_state . contains ( r # "node = "Seth""# ) ) ;
assert! ( ! merged_state . contains ( " SETTING_THAT_DOESNT_EXIST_ANYMORE " ) ) ;
assert! ( merged_state . contains ( " 44hintoFpuo3ugKfcqJvh5BmrsTRpnTasJmetKC4VXCt6QDtbHVuixdTtsm6Ptp7Y8haXnJ6j8Gj2dra8CKy5ewz7Vi9CYW " ) ) ;
}
2022-12-26 22:37:45 +00:00
#[ test ]
fn create_and_serde_gupax_p2pool_api ( ) {
use crate ::disk ::GupaxP2poolApi ;
2022-12-31 18:47:41 +00:00
use crate ::xmr ::PayoutOrd ;
2022-12-28 21:04:26 +00:00
use crate ::xmr ::AtomicUnit ;
2022-12-26 22:37:45 +00:00
// Get API dir, fill paths.
let mut api = GupaxP2poolApi ::new ( ) ;
let mut path = crate ::disk ::get_gupax_data_path ( ) . unwrap ( ) ;
path . push ( crate ::disk ::GUPAX_P2POOL_API_DIRECTORY ) ;
GupaxP2poolApi ::fill_paths ( & mut api , & path ) ;
println! ( " {:#?} " , api ) ;
2022-12-28 21:04:26 +00:00
// Create, write some fake data.
2022-12-26 22:37:45 +00:00
GupaxP2poolApi ::create_all_files ( & path ) . unwrap ( ) ;
2022-12-29 17:12:12 +00:00
api . log = " NOTICE 2022-01-27 01:30:23.1377 P2Pool You received a payout of 0.000000000001 XMR in block 2642816 " . to_string ( ) ;
api . payout_u64 = 1 ;
api . xmr = AtomicUnit ::from_u64 ( 2 ) ;
2023-04-19 15:31:36 +00:00
let ( date , atomic_unit , block ) = PayoutOrd ::parse_raw_payout_line ( & api . log ) ;
2022-12-31 18:47:41 +00:00
let formatted_log_line = GupaxP2poolApi ::format_payout ( & date , & atomic_unit , & block ) ;
GupaxP2poolApi ::write_to_all_files ( & api , & formatted_log_line ) . unwrap ( ) ;
2022-12-26 22:37:45 +00:00
println! ( " AFTER WRITE: {:#?} " , api ) ;
2022-12-28 21:04:26 +00:00
// Read
2022-12-26 22:37:45 +00:00
GupaxP2poolApi ::read_all_files_and_update ( & mut api ) . unwrap ( ) ;
println! ( " AFTER READ: {:#?} " , api ) ;
// Assert that the file read mutated the internal struct correctly.
2022-12-29 17:12:12 +00:00
assert_eq! ( api . payout_u64 , 1 ) ;
assert_eq! ( api . xmr . to_u64 ( ) , 2 ) ;
2022-12-28 21:04:26 +00:00
assert! ( ! api . payout_ord . is_empty ( ) ) ;
2022-12-31 18:47:41 +00:00
assert! ( api . log . contains ( " 2022-01-27 01:30:23.1377 | 0.000000000001 XMR | Block 2,642,816 " ) ) ;
2022-12-26 22:37:45 +00:00
}
2022-12-27 17:58:46 +00:00
#[ test ]
fn convert_hash ( ) {
use crate ::disk ::Hash ;
let hash = 1.0 ;
assert_eq! ( Hash ::convert ( hash , Hash ::Hash , Hash ::Hash ) , 1.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Hash , Hash ::Kilo ) , 0.001 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Hash , Hash ::Mega ) , 0.000_001 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Hash , Hash ::Giga ) , 0.000_000_001 ) ;
let hash = 1.0 ;
assert_eq! ( Hash ::convert ( hash , Hash ::Kilo , Hash ::Hash ) , 1_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Kilo , Hash ::Kilo ) , 1.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Kilo , Hash ::Mega ) , 0.001 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Kilo , Hash ::Giga ) , 0.000_001 ) ;
let hash = 1.0 ;
assert_eq! ( Hash ::convert ( hash , Hash ::Mega , Hash ::Hash ) , 1_000_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Mega , Hash ::Kilo ) , 1_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Mega , Hash ::Mega ) , 1.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Mega , Hash ::Giga ) , 0.001 ) ;
let hash = 1.0 ;
assert_eq! ( Hash ::convert ( hash , Hash ::Giga , Hash ::Hash ) , 1_000_000_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Giga , Hash ::Kilo ) , 1_000_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Giga , Hash ::Mega ) , 1_000.0 ) ;
assert_eq! ( Hash ::convert ( hash , Hash ::Giga , Hash ::Giga ) , 1.0 ) ;
}
2022-12-17 22:17:26 +00:00
}