1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
pub mod block;
pub mod block_sums;
pub mod committed;
pub mod compact_block;
pub mod hash;
pub mod id;
pub mod merkle_proof;
pub mod pmmr;
pub mod transaction;
pub mod verifier_cache;
use crate::consensus::GRIN_BASE;
use util::secp::pedersen::Commitment;
pub use self::block::*;
pub use self::block_sums::*;
pub use self::committed::Committed;
pub use self::compact_block::*;
pub use self::id::ShortId;
pub use self::transaction::*;
#[derive(Fail, Debug)]
pub enum Error {
#[fail(display = "Amount string was invalid")]
InvalidAmountString,
}
pub fn amount_from_hr_string(amount: &str) -> Result<u64, Error> {
if amount.find(',').is_some() {
return Err(Error::InvalidAmountString);
}
let (grins, ngrins) = match amount.find('.') {
None => (parse_grins(amount)?, 0),
Some(pos) => {
let (gs, tail) = amount.split_at(pos);
(parse_grins(gs)?, parse_ngrins(&tail[1..])?)
}
};
Ok(grins * GRIN_BASE + ngrins)
}
fn parse_grins(amount: &str) -> Result<u64, Error> {
if amount == "" {
Ok(0)
} else {
amount
.parse::<u64>()
.map_err(|_| Error::InvalidAmountString)
}
}
lazy_static! {
static ref WIDTH: usize = (GRIN_BASE as f64).log(10.0) as usize + 1;
}
fn parse_ngrins(amount: &str) -> Result<u64, Error> {
let amount = if amount.len() > *WIDTH {
&amount[..*WIDTH]
} else {
amount
};
format!("{:0<width$}", amount, width = WIDTH)
.parse::<u64>()
.map_err(|_| Error::InvalidAmountString)
}
pub fn amount_to_hr_string(amount: u64, truncate: bool) -> String {
let amount = (amount as f64 / GRIN_BASE as f64) as f64;
let hr = format!("{:.*}", WIDTH, amount);
if truncate {
let nzeros = hr.chars().rev().take_while(|x| x == &'0').count();
if nzeros < *WIDTH {
return hr.trim_end_matches('0').to_string();
} else {
return format!("{}0", hr.trim_end_matches('0'));
}
}
hr
}
#[cfg(test)]
mod test {
use super::*;
#[test]
pub fn test_amount_from_hr() {
assert!(50123456789 == amount_from_hr_string("50.123456789").unwrap());
assert!(50123456789 == amount_from_hr_string("50.1234567899").unwrap());
assert!(50 == amount_from_hr_string(".000000050").unwrap());
assert!(1 == amount_from_hr_string(".000000001").unwrap());
assert!(0 == amount_from_hr_string(".0000000009").unwrap());
assert!(500_000_000_000 == amount_from_hr_string("500").unwrap());
assert!(
5_000_000_000_000_000_000 == amount_from_hr_string("5000000000.00000000000").unwrap()
);
assert!(66_600_000_000 == amount_from_hr_string("66.6").unwrap());
assert!(66_000_000_000 == amount_from_hr_string("66.").unwrap());
}
#[test]
pub fn test_amount_to_hr() {
assert!("50.123456789" == amount_to_hr_string(50123456789, false));
assert!("50.123456789" == amount_to_hr_string(50123456789, true));
assert!("0.000000050" == amount_to_hr_string(50, false));
assert!("0.00000005" == amount_to_hr_string(50, true));
assert!("0.000000001" == amount_to_hr_string(1, false));
assert!("0.000000001" == amount_to_hr_string(1, true));
assert!("500.000000000" == amount_to_hr_string(500_000_000_000, false));
assert!("500.0" == amount_to_hr_string(500_000_000_000, true));
assert!("5000000000.000000000" == amount_to_hr_string(5_000_000_000_000_000_000, false));
assert!("5000000000.0" == amount_to_hr_string(5_000_000_000_000_000_000, true));
assert!("66.6" == amount_to_hr_string(66600000000, true));
}
}